NetSharp

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

commit b4cd587115d9e71dabbaf571e1e393557ec0d97f
parent da9d92cdd92bfe14ac802c9eda7fc32ddca5c364
Author: Mikolaj Lenczewski <mikolaj.lenczewski308@gmail.com>
Date:   Tue,  7 Apr 2020 14:40:12 +0100

Started architecture rework, with basic TCP and UDP echo servers implemented.

Diffstat:
DNetSharp/NetSharp/Connection.cs | 356-------------------------------------------------------------------------------
DNetSharp/NetSharp/ConnectionBase.cs | 467-------------------------------------------------------------------------------
DNetSharp/NetSharp/ConnectionBuilder.cs | 192-------------------------------------------------------------------------------
ANetSharp/NetSharp/Deprecated/Builtin/ConnectPacket.cs | 33+++++++++++++++++++++++++++++++++
ANetSharp/NetSharp/Deprecated/Builtin/ConnectResponsePacket.cs | 36++++++++++++++++++++++++++++++++++++
ANetSharp/NetSharp/Deprecated/Builtin/DataPacket.cs | 56++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ANetSharp/NetSharp/Deprecated/Builtin/DataResponsePacket.cs | 59+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ANetSharp/NetSharp/Deprecated/Builtin/DisconnectPacket.cs | 33+++++++++++++++++++++++++++++++++
ANetSharp/NetSharp/Deprecated/Builtin/PingPacket.cs | 33+++++++++++++++++++++++++++++++++
ANetSharp/NetSharp/Deprecated/Builtin/PingResponsePacket.cs | 36++++++++++++++++++++++++++++++++++++
ANetSharp/NetSharp/Deprecated/Builtin/SimpleDataPacket.cs | 56++++++++++++++++++++++++++++++++++++++++++++++++++++++++
MNetSharp/NetSharp/Deprecated/Client.cs | 5+++--
ANetSharp/NetSharp/Deprecated/Connection.cs | 354+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ANetSharp/NetSharp/Deprecated/ConnectionBase.cs | 466+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ANetSharp/NetSharp/Deprecated/ConnectionBuilder.cs | 187+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ANetSharp/NetSharp/Deprecated/ConnectionBuilderExtensions.cs | 28++++++++++++++++++++++++++++
ANetSharp/NetSharp/Deprecated/ConnectionExtensions.cs | 74++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ANetSharp/NetSharp/Deprecated/Constants.cs | 16++++++++++++++++
ANetSharp/NetSharp/Deprecated/CryptographyHelpers.cs | 97+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ANetSharp/NetSharp/Deprecated/Logger.cs | 195+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ANetSharp/NetSharp/Deprecated/NetworkErrorCode.cs | 19+++++++++++++++++++
ANetSharp/NetSharp/Deprecated/NetworkPacket.cs | 219+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ANetSharp/NetSharp/Deprecated/PacketPipeline.cs | 66++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ANetSharp/NetSharp/Deprecated/PacketPipelineBuilder.cs | 88+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ANetSharp/NetSharp/Deprecated/PacketRegistry.cs | 286+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ANetSharp/NetSharp/Deprecated/PacketTypeIdAttribute.cs | 27+++++++++++++++++++++++++++
ANetSharp/NetSharp/Deprecated/RemoteSocketClient.cs | 74++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ANetSharp/NetSharp/Deprecated/RingBuffer.cs | 49+++++++++++++++++++++++++++++++++++++++++++++++++
MNetSharp/NetSharp/Deprecated/SerialisedPacket.cs | 1-
MNetSharp/NetSharp/Deprecated/Server.cs | 6+++---
MNetSharp/NetSharp/Deprecated/ServerClientConnection.cs | 2--
MNetSharp/NetSharp/Deprecated/ServerExtensions.cs | 1-
ANetSharp/NetSharp/Deprecated/SocketAcceptor.cs | 282+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ANetSharp/NetSharp/Deprecated/SocketClient.cs | 133+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ANetSharp/NetSharp/Deprecated/SocketOperationTokens.cs | 81+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ANetSharp/NetSharp/Deprecated/SocketOperations.cs | 470+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ANetSharp/NetSharp/Deprecated/SocketReader.cs | 154+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ANetSharp/NetSharp/Deprecated/SocketServer.cs | 122+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ANetSharp/NetSharp/Deprecated/SocketWriter.cs | 145+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
MNetSharp/NetSharp/Deprecated/TcpClient.cs | 6+++---
MNetSharp/NetSharp/Deprecated/TcpServer.cs | 6+++---
MNetSharp/NetSharp/Deprecated/UdpClient.cs | 6+++---
MNetSharp/NetSharp/Deprecated/UdpServer.cs | 6+++---
DNetSharp/NetSharp/Extensions/ConnectionBuilderExtensions.cs | 30------------------------------
DNetSharp/NetSharp/Extensions/ConnectionExtensions.cs | 73-------------------------------------------------------------------------
DNetSharp/NetSharp/Logging/Logger.cs | 195-------------------------------------------------------------------------------
MNetSharp/NetSharp/NetSharp.csproj | 10++++++----
MNetSharp/NetSharp/NetSharp.xml | 2301++++++++++++++++++++++++++++++++++++++++---------------------------------------
DNetSharp/NetSharp/Packets/Builtin/ConnectPacket.cs | 34----------------------------------
DNetSharp/NetSharp/Packets/Builtin/ConnectResponsePacket.cs | 37-------------------------------------
DNetSharp/NetSharp/Packets/Builtin/DataPacket.cs | 57---------------------------------------------------------
DNetSharp/NetSharp/Packets/Builtin/DataResponsePacket.cs | 60------------------------------------------------------------
DNetSharp/NetSharp/Packets/Builtin/DisconnectPacket.cs | 34----------------------------------
DNetSharp/NetSharp/Packets/Builtin/PingPacket.cs | 34----------------------------------
DNetSharp/NetSharp/Packets/Builtin/PingResponsePacket.cs | 37-------------------------------------
DNetSharp/NetSharp/Packets/Builtin/SimpleDataPacket.cs | 57---------------------------------------------------------
DNetSharp/NetSharp/Packets/NetworkErrorCode.cs | 19-------------------
MNetSharp/NetSharp/Packets/NetworkPacket.cs | 228+++++++++++++++++++------------------------------------------------------------
DNetSharp/NetSharp/Packets/PacketRegistry.cs | 288-------------------------------------------------------------------------------
DNetSharp/NetSharp/Packets/PacketTypeIdAttribute.cs | 28----------------------------
DNetSharp/NetSharp/Pipelines/PacketPipeline.cs | 66------------------------------------------------------------------
DNetSharp/NetSharp/Pipelines/PacketPipelineBuilder.cs | 88-------------------------------------------------------------------------------
ANetSharp/NetSharp/Sockets/Datagram/DatagramSocketClient.cs | 13+++++++++++++
ANetSharp/NetSharp/Sockets/Datagram/DatagramSocketServer.cs | 170+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
DNetSharp/NetSharp/Sockets/SocketAcceptor.cs | 281-------------------------------------------------------------------------------
ANetSharp/NetSharp/Sockets/SocketAsyncOperationTokens.cs | 92+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ANetSharp/NetSharp/Sockets/SocketAsyncOperations.cs | 308+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
MNetSharp/NetSharp/Sockets/SocketClient.cs | 90++++++++++++++++++++-----------------------------------------------------------
ANetSharp/NetSharp/Sockets/SocketConnection.cs | 45+++++++++++++++++++++++++++++++++++++++++++++
DNetSharp/NetSharp/Sockets/SocketOperations.cs | 428-------------------------------------------------------------------------------
DNetSharp/NetSharp/Sockets/SocketReader.cs | 153-------------------------------------------------------------------------------
MNetSharp/NetSharp/Sockets/SocketServer.cs | 92++++++++++++++-----------------------------------------------------------------
DNetSharp/NetSharp/Sockets/SocketWriter.cs | 145-------------------------------------------------------------------------------
ANetSharp/NetSharp/Sockets/Stream/StreamSocketClient.cs | 24++++++++++++++++++++++++
ANetSharp/NetSharp/Sockets/Stream/StreamSocketServer.cs | 146+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
MNetSharp/NetSharp/Utils/BiDictionary.cs | 2+-
DNetSharp/NetSharp/Utils/Constants.cs | 16----------------
MNetSharp/NetSharp/Utils/Conversion/EndianAwareBitConverter.cs | 2+-
DNetSharp/NetSharp/Utils/CryptographyHelpers.cs | 97-------------------------------------------------------------------------------
DNetSharp/NetSharp/Utils/RingBuffer.cs | 51---------------------------------------------------
MNetSharp/NetSharpExamples/Program.cs | 226+++++++++++++++++++++++++++++--------------------------------------------------
81 files changed, 6139 insertions(+), 4946 deletions(-)

diff --git a/NetSharp/NetSharp/Connection.cs b/NetSharp/NetSharp/Connection.cs @@ -1,355 +0,0 @@ -using System; -using System.Buffers; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.IO; -using System.Net; -using System.Net.Sockets; -using System.Threading; -using System.Threading.Channels; -using System.Threading.Tasks; -using Microsoft.Extensions.ObjectPool; -using NetSharp.Deprecated; -using NetSharp.Logging; -using NetSharp.Packets; -using NetSharp.Pipelines; -using NetSharp.Utils; - -namespace NetSharp -{ - /// <summary> - /// Encapsulates a connection capable of receiving packets and responding to them with registered packet handlers. - /// </summary> - public sealed partial class Connection : IDisposable - { - private readonly HashSet<EndPoint> datagramConnections; - private readonly Channel<(EndPoint origin, Memory<byte> packet)> incomingPacketChannel; - - /// <summary> - /// Pipeline to convert incoming byte buffers to <see cref="NetworkPacket"/> instances. - /// </summary> - private readonly PacketPipeline<Memory<byte>, Memory<byte>, NetworkPacket> incomingPacketPipeline; - - /// <summary> - /// Lock synchronisation object for the <see cref="logger"/> variable. - /// </summary> - private readonly object loggerLockObject = new object(); - - private readonly Channel<(EndPoint destination, NetworkPacket packet)> outgoingPacketChannel; - - /// <summary> - /// Pipeline to convert outgoing <see cref="NetworkPacket"/> instances to a byte buffer for sending. - /// </summary> - private readonly PacketPipeline<NetworkPacket, Memory<byte>, Memory<byte>> outgoingPacketPipeline; - - private readonly Channel<(EndPoint origin, IRequestPacket request)> requestChannel; - - /// <summary> - /// Cancellation token which allows observing the shutdown of the server. It is set when <see cref="ShutdownServer"/> is called. - /// </summary> - private readonly CancellationToken ServerShutdownToken; - - private readonly ConcurrentDictionary<EndPoint, Socket> streamConnections; - - /// <summary> - /// A logger object allowing for writing debug messages to an output stream. - /// </summary> - private Logger logger; - - /// <summary> - /// Destroys a <see cref="Connection"/> class instance, freeing all managed resources. - /// </summary> - ~Connection() - { - Dispose(false); - } - - private async Task AcceptorWork(object shutdownToken) - { - CancellationToken cancellationToken = (CancellationToken)shutdownToken; - - logger.LogMessage("Started stream acceptor task."); - - async Task StreamListenerWork(object clientArgsObj) - { - Socket clientSocket = (Socket)clientArgsObj; - EndPoint clientEndPoint = clientSocket.RemoteEndPoint; - - logger.LogMessage($"Client handler started for {clientEndPoint}"); - - while (!cancellationToken.IsCancellationRequested) - { - // TODO: implement receive buffer pooling - byte[] receiveBuffer = new byte[NetworkPacket.PacketSize]; - Memory<byte> receiveBufferMemory = new Memory<byte>(receiveBuffer); - - TransmissionResult result = - await DoReceiveFromAsync(clientSocket, clientEndPoint, SocketFlags.None, receiveBufferMemory, cancellationToken); - - if (result.Count == 0) - { - break; - } - - await incomingPacketChannel.Writer.WriteAsync((result.RemoteEndPoint, receiveBufferMemory), - cancellationToken); - } - - logger.LogMessage($"Client handler stopped for {clientEndPoint}"); - - await DoDisconnectAsync(clientSocket, cancellationToken); - - clientSocket.Shutdown(SocketShutdown.Both); - clientSocket.Close(1); - } - - streamSocket.Listen(MaximumConnectionBacklog); - - while (!cancellationToken.IsCancellationRequested) - { - Socket clientSocket = await DoAcceptAsync(streamSocket, cancellationToken); - - if (!streamConnections.ContainsKey(clientSocket.RemoteEndPoint)) - { - streamConnections[clientSocket.RemoteEndPoint] = clientSocket; - - await Task.Factory.StartNew(StreamListenerWork, streamConnections[clientSocket.RemoteEndPoint], ServerShutdownToken); - } - else - { - logger.LogWarning($"Accepted duplicate connection from {clientSocket.RemoteEndPoint}"); - } - } - - logger.LogMessage("Stopped stream acceptor task."); - } - - private async Task DatagramListenerWork(object shutdownToken) - { - CancellationToken cancellationToken = (CancellationToken)shutdownToken; - - logger.LogMessage("Started datagram listener task."); - - while (!cancellationToken.IsCancellationRequested) - { - // TODO: implement receive buffer pooling - byte[] receiveBuffer = new byte[NetworkPacket.PacketSize]; - Memory<byte> receiveBufferMemory = new Memory<byte>(receiveBuffer); - - TransmissionResult result = - await DoReceiveFromAsync(datagramSocket, AnyRemoteEndPoint, SocketFlags.None, - receiveBufferMemory, cancellationToken); - - if (!datagramConnections.Contains(result.RemoteEndPoint)) - { - datagramConnections.Add(result.RemoteEndPoint); - } - - await incomingPacketChannel.Writer.WriteAsync((result.RemoteEndPoint, receiveBufferMemory), cancellationToken); - } - - logger.LogMessage("Stopped datagram listener task."); - } - - private async Task IncomingPacketHandlerWork(object shutdownToken) - { - CancellationToken cancellationToken = (CancellationToken)shutdownToken; - - logger.LogMessage("Started incoming packet handler task."); - - while (!cancellationToken.IsCancellationRequested) - { - (EndPoint origin, Memory<byte> packet) = - await incomingPacketChannel.Reader.ReadAsync(cancellationToken); - - NetworkPacket deserialisedRequest = incomingPacketPipeline.ProcessPacket(packet); - - // TODO implement deserialisation according to registered packet deserialisers - - // TODO: write to requestChannel, not to outgoingPacketChannel - await outgoingPacketChannel.Writer.WriteAsync((origin, deserialisedRequest), cancellationToken); - } - - logger.LogMessage("Stopped incoming packet handler task."); - } - - private async Task OutgoingPacketHandlerWork(object shutdownToken) - { - CancellationToken cancellationToken = (CancellationToken)shutdownToken; - - logger.LogMessage("Started outgoing packet handler task."); - - while (!cancellationToken.IsCancellationRequested) - { - (EndPoint destination, NetworkPacket packet) = - await outgoingPacketChannel.Reader.ReadAsync(cancellationToken); - - Memory<byte> serialisedResponse = outgoingPacketPipeline.ProcessPacket(packet); - - if (streamConnections.ContainsKey(destination)) - { - Socket streamConnection = streamConnections[destination]; - - await DoSendToAsync(streamConnection, destination, SocketFlags.None, - serialisedResponse, cancellationToken); - } - else if (datagramConnections.Contains(destination)) - { - await DoSendToAsync(datagramSocket, destination, SocketFlags.None, - serialisedResponse, cancellationToken); - } - else - { - logger.LogWarning($"Packet destined for unknown destination: {destination}"); - } - } - - logger.LogMessage("Stopped outgoing packet handler task."); - } - - private async Task RequestHandlerInvocationWork(object shutdownToken) - { - CancellationToken cancellationToken = (CancellationToken)shutdownToken; - - logger.LogMessage("Started request handler invocation task."); - - while (!cancellationToken.IsCancellationRequested) - { - (EndPoint origin, IRequestPacket request) = await requestChannel.Reader.ReadAsync(cancellationToken); - - // TODO: implement proper request handling, and conversion to IResponsePacket<IRequestPacke> - - NetworkPacket serialisedResponsePacket = new NetworkPacket(); - - await outgoingPacketChannel.Writer.WriteAsync((origin, serialisedResponsePacket), cancellationToken); - } - - logger.LogMessage("Stopped request handler invocation task."); - } - - internal Connection( - PacketPipeline<Memory<byte>, Memory<byte>, NetworkPacket> incomingPacketPipeline, - PacketPipeline<NetworkPacket, Memory<byte>, Memory<byte>> outgoingPacketPipeline, - int objectPoolSize = 10, bool preallocateBuffers = false, Stream? loggingStream = default, - LogLevel minimumLoggedSeverity = LogLevel.Info) - { - serverShutdownTokenSource = new CancellationTokenSource(); - ServerShutdownToken = serverShutdownTokenSource.Token; - - streamSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); - datagramSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); - - sendToBufferPool = ArrayPool<byte>.Create(NetworkPacket.PacketSize, objectPoolSize); - receiveFromBufferPool = ArrayPool<byte>.Create(NetworkPacket.PacketSize, objectPoolSize); - - clientSocketArgsPool = - new LeakTrackingObjectPool<SocketAsyncEventArgs>( - new DefaultObjectPool<SocketAsyncEventArgs>(new DefaultPooledObjectPolicy<SocketAsyncEventArgs>(), - objectPoolSize)); - receiveArgsPool = - new LeakTrackingObjectPool<SocketAsyncEventArgs>( - new DefaultObjectPool<SocketAsyncEventArgs>(new DefaultPooledObjectPolicy<SocketAsyncEventArgs>(), - objectPoolSize)); - sendArgsPool = - new LeakTrackingObjectPool<SocketAsyncEventArgs>( - new DefaultObjectPool<SocketAsyncEventArgs>(new DefaultPooledObjectPolicy<SocketAsyncEventArgs>(), - objectPoolSize)); - - for (int i = 0; i < objectPoolSize; i++) - { - SocketAsyncEventArgs clientArgs = new SocketAsyncEventArgs(); - clientArgs.Completed += HandleIOCompleted; - clientSocketArgsPool.Return(clientArgs); - - SocketAsyncEventArgs receiveArgs = new SocketAsyncEventArgs(); - receiveArgs.Completed += HandleIOCompleted; - receiveArgsPool.Return(receiveArgs); - - SocketAsyncEventArgs sendArgs = new SocketAsyncEventArgs(); - sendArgs.Completed += HandleIOCompleted; - sendArgsPool.Return(sendArgs); - } - - if (preallocateBuffers) - { - //TODO: Preallocate buffers someday - } - - streamConnections = new ConcurrentDictionary<EndPoint, Socket>(); - datagramConnections = new HashSet<EndPoint>(); - - this.incomingPacketPipeline = incomingPacketPipeline; - BoundedChannelOptions incomingChannelOptions = new BoundedChannelOptions(MaximumPacketBacklog) - { - FullMode = BoundedChannelFullMode.DropOldest, - SingleReader = true, - SingleWriter = false, - }; - incomingPacketChannel = Channel.CreateBounded<(EndPoint origin, Memory<byte> packet)>(incomingChannelOptions); - - BoundedChannelOptions requestChannelOptions = new BoundedChannelOptions(MaximumPacketBacklog) - { - FullMode = BoundedChannelFullMode.DropOldest, - SingleReader = true, - SingleWriter = true, - }; - requestChannel = Channel.CreateBounded<(EndPoint origin, IRequestPacket request)>(requestChannelOptions); - - this.outgoingPacketPipeline = outgoingPacketPipeline; - BoundedChannelOptions outgoingChannelOptions = new BoundedChannelOptions(MaximumPacketBacklog) - { - FullMode = BoundedChannelFullMode.DropOldest, - SingleReader = true, - SingleWriter = true, - }; - outgoingPacketChannel = Channel.CreateBounded<(EndPoint destination, NetworkPacket packet)>(outgoingChannelOptions); - - logger = new Logger(loggingStream ?? Stream.Null, minimumLoggedSeverity); - } - - /// <summary> - /// Makes the connection listen for incoming request packets, and handle them according to registered packet handler delegates. - /// This work can be cancelled by calling <see cref="ShutdownServer"/>. - /// </summary> - /// <returns>The task representing the connection work.</returns> - public Task RunServerAsync() - { - logger.LogMessage("Starting stream acceptor task..."); - Task acceptorThread = - Task.Factory.StartNew(AcceptorWork, ServerShutdownToken, ServerShutdownToken, TaskCreationOptions.LongRunning, - TaskScheduler.Default).Result; - - logger.LogMessage("Starting datagram listener task..."); - Task datagramListenerThread = - Task.Factory.StartNew(DatagramListenerWork, ServerShutdownToken, ServerShutdownToken, TaskCreationOptions.LongRunning, - TaskScheduler.Default); - - logger.LogMessage("Starting incoming packet handler task..."); - Task incomingPacketHandlerThread = - Task.Factory.StartNew(IncomingPacketHandlerWork, ServerShutdownToken, ServerShutdownToken, TaskCreationOptions.LongRunning, - TaskScheduler.Default); - - logger.LogMessage("Starting request packet invocation task..."); - Task requestHandlerInvocationThread = - Task.Factory.StartNew(RequestHandlerInvocationWork, ServerShutdownToken, ServerShutdownToken, TaskCreationOptions.LongRunning, - TaskScheduler.Default).Result; - - logger.LogMessage("Starting outgoing packet handler task..."); - Task outgoingPacketHandlerThread = - Task.Factory.StartNew(OutgoingPacketHandlerWork, ServerShutdownToken, ServerShutdownToken, TaskCreationOptions.LongRunning, - TaskScheduler.Default); - - return Task.WhenAll(acceptorThread, datagramListenerThread, - incomingPacketHandlerThread, requestHandlerInvocationThread, outgoingPacketHandlerThread); - } - - /// <summary> - /// Shuts down the connection, and releases managed and unmanaged resources. - /// </summary> - public void ShutdownServer() - { - logger.LogMessage("Signalling shutdown to all client connection handlers..."); - serverShutdownTokenSource.Cancel(); - } - } -} -\ No newline at end of file diff --git a/NetSharp/NetSharp/ConnectionBase.cs b/NetSharp/NetSharp/ConnectionBase.cs @@ -1,466 +0,0 @@ -using System; -using System.Buffers; -using System.Data; -using System.IO; -using System.Net; -using System.Net.Sockets; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Extensions.ObjectPool; -using NetSharp.Logging; -using NetSharp.Packets; -using NetSharp.Utils; - -namespace NetSharp -{ - /// <summary> - /// Implements low-level network access on top of which the rest of the connection is built upon. - /// </summary> - public sealed partial class Connection : IDisposable - { - /// <summary> - /// Represents any remote endpoint for datagram operations. - /// </summary> - private static readonly EndPoint AnyRemoteEndPoint = new IPEndPoint(IPAddress.Any, 0); - - private readonly ObjectPool<SocketAsyncEventArgs> clientSocketArgsPool; - - private readonly Socket datagramSocket; - - private readonly ObjectPool<SocketAsyncEventArgs> receiveArgsPool; - - private readonly ArrayPool<byte> receiveFromBufferPool; - - private readonly ObjectPool<SocketAsyncEventArgs> sendArgsPool; - - private readonly ArrayPool<byte> sendToBufferPool; - - private readonly CancellationTokenSource serverShutdownTokenSource; - - private readonly Socket streamSocket; - - /// <summary> - /// Disposes of the managed and unmanaged resources held by this instance. - /// </summary> - /// <param name="disposing">Whether this method is called by <see cref="Dispose()"/> or by the finaliser.</param> - private void Dispose(bool disposing) - { - if (disposing) - { - serverShutdownTokenSource.Cancel(); - serverShutdownTokenSource.Dispose(); - - streamSocket.Dispose(); - datagramSocket.Dispose(); - } - } - - /// <summary> - /// Provides an awaitable wrapper around an asynchronous socket accept operation. - /// </summary> - /// <param name="serverSocket">The socket which should be used to accept an incoming connection attempt.</param> - /// <param name="cancellationToken">The cancellation token to observe for the operation.</param> - /// <returns>The accepted socket.</returns> - private async Task<Socket> DoAcceptAsync(Socket serverSocket, CancellationToken cancellationToken = default) - { - TaskCompletionSource<Socket> tcs = new TaskCompletionSource<Socket>(); - - cancellationToken.Register(() => tcs.SetCanceled()); - - Task<Socket> task = serverSocket.AcceptAsync(); - Task<Socket> completedTask = await Task.WhenAny(task, tcs.Task); - - if (completedTask == task) - { - Socket result = await task; - - tcs.SetResult(result); - } - - return await tcs.Task; - } - - /// <summary> - /// Provides an awaitable wrapper around an asynchronous socket connect operation. - /// </summary> - /// <param name="socket">The socket which should asynchronously connect to the remote endpoint.</param> - /// <param name="remoteEndPoint">The remote endpoint to which the socket should connect.</param> - /// <param name="cancellationToken">The cancellation token to observe for the operation.</param> - private async Task DoConnectAsync(Socket socket, EndPoint remoteEndPoint, CancellationToken cancellationToken = default) - { - TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>(); - - cancellationToken.Register(() => tcs.SetCanceled()); - - Task task = socket.ConnectAsync(remoteEndPoint); - Task completedTask = await Task.WhenAny(task, tcs.Task); - - if (completedTask == task) - { - await task; - tcs.SetResult(true); - } - - await tcs.Task; - } - - /// <summary> - /// Provides an awaitable wrapper around an asynchronous socket disconnect operation. - /// </summary> - /// <param name="connectedSocket">The socket which should asynchronously disconnect from its remote endpoint.</param> - /// <param name="cancellationToken">The cancellation token to observe for the operation.</param> - private Task DoDisconnectAsync(Socket connectedSocket, CancellationToken cancellationToken = default) - { - return Task.Factory.StartNew(() => - { - connectedSocket.Disconnect(true); - }, cancellationToken); - } - - /// <summary> - /// Provides an awaitable wrapper around an asynchronous socket receive operation. - /// </summary> - /// <param name="listenerSocket">The socket which should receive data from the remote endpoint.</param> - /// <param name="remoteEndPoint">The remove endpoint from which data should be received.</param> - /// <param name="socketFlags">The socket flags associated with the receive operation.</param> - /// <param name="inputBuffer">The memory buffer into which received data will be stored.</param> - /// <param name="cancellationToken">The cancellation token to observe for the operation.</param> - /// <returns>The result of the receive operation from the remote endpoint.</returns> - private Task<TransmissionResult> DoReceiveFromAsync(Socket listenerSocket, EndPoint remoteEndPoint, SocketFlags socketFlags, - Memory<byte> inputBuffer, CancellationToken cancellationToken = default) - { - TaskCompletionSource<TransmissionResult> tcs = new TaskCompletionSource<TransmissionResult>(); - - byte[] rentedReceiveFromBuffer = receiveFromBufferPool.Rent(NetworkPacket.PacketSize); - Memory<byte> rentedReceiveFromBufferMemory = new Memory<byte>(rentedReceiveFromBuffer); - - SocketAsyncEventArgs clientArgs = receiveArgsPool.Get(); - clientArgs.SetBuffer(rentedReceiveFromBufferMemory); - clientArgs.SocketFlags = socketFlags; - clientArgs.RemoteEndPoint = remoteEndPoint; - clientArgs.UserToken = new AsyncReadToken(rentedReceiveFromBuffer, inputBuffer, tcs, cancellationToken); - - // if the receive operation doesn't complete synchronously, returns the awaitable task - if (listenerSocket.ReceiveFromAsync(clientArgs)) return tcs.Task; - - clientArgs.MemoryBuffer.CopyTo(inputBuffer); - - TransmissionResult result = new TransmissionResult(clientArgs); - - receiveFromBufferPool.Return(rentedReceiveFromBuffer, true); - receiveArgsPool.Return(clientArgs); - - return Task.FromResult(result); - } - - /// <summary> - /// Provides an awaitable wrapper around an asynchronous socket send operation. - /// </summary> - /// <param name="transmitterSocket">The socket which should send the data to the remote endpoint.</param> - /// <param name="remoteEndPoint">The remote endpoint to which data should be written.</param> - /// <param name="socketFlags">The socket flags associated with the send operation.</param> - /// <param name="outputBuffer">The data buffer which should be sent.</param> - /// <param name="cancellationToken">The cancellation token to observe for the operation.</param> - /// <returns>The result of the send operation to the remote endpoint.</returns> - private ValueTask<int> DoSendToAsync(Socket transmitterSocket, EndPoint remoteEndPoint, SocketFlags socketFlags, - Memory<byte> outputBuffer, CancellationToken cancellationToken = default) - { - TaskCompletionSource<int> tcs = new TaskCompletionSource<int>(); - - byte[] rentedSendToBuffer = sendToBufferPool.Rent(NetworkPacket.PacketSize); - Memory<byte> rentedSendToBufferMemory = new Memory<byte>(rentedSendToBuffer); - - outputBuffer.CopyTo(rentedSendToBufferMemory); - - SocketAsyncEventArgs clientArgs = sendArgsPool.Get(); - clientArgs.SetBuffer(rentedSendToBufferMemory); - clientArgs.SocketFlags = socketFlags; - clientArgs.RemoteEndPoint = remoteEndPoint; - clientArgs.UserToken = new AsyncWriteToken(rentedSendToBuffer, tcs, cancellationToken); - - /* NOT WORKING, NEED SOLUTION AT SOME POINT!!! - // register cleanup action for when the cancellation token is thrown - cancellationToken.Register(() => - { - tcs.SetCanceled(); - - sendBufferPool.Return(rentedSendToBuffer, true); - - //TODO this is probably a hideous solution. find a better one - args.Completed -= HandleIOCompleted; - args.Dispose(); - - SocketAsyncEventArgs newArgs = new SocketAsyncEventArgs(); - newArgs.Completed += HandleIOCompleted; - sendAsyncEventArgsPool.Return(newArgs); - }); - */ - - // if the send operation doesn't complete synchronously, return the awaitable task - if (transmitterSocket.SendToAsync(clientArgs)) return new ValueTask<int>(tcs.Task); - - int result = clientArgs.BytesTransferred; - - sendToBufferPool.Return(rentedSendToBuffer, true); - sendArgsPool.Return(clientArgs); - - return new ValueTask<int>(result); - } - - private void HandleIOCompleted(object? sender, SocketAsyncEventArgs args) - { - switch (args.LastOperation) - { - case SocketAsyncOperation.SendTo: - AsyncWriteToken asyncSendToToken = (AsyncWriteToken)args.UserToken; - - if (asyncSendToToken.CancellationToken.IsCancellationRequested) - { - asyncSendToToken.CompletionSource.SetCanceled(); - } - else - { - if (args.SocketError != SocketError.Success) - { - asyncSendToToken.CompletionSource.SetException( - new SocketException((int)args.SocketError)); - } - else - { - asyncSendToToken.CompletionSource.SetResult(args.BytesTransferred); - } - } - - sendToBufferPool.Return(asyncSendToToken.RentedBuffer, true); - sendArgsPool.Return(args); - - break; - - case SocketAsyncOperation.ReceiveFrom: - AsyncReadToken asyncReceiveFromToken = (AsyncReadToken)args.UserToken; - - if (asyncReceiveFromToken.CancellationToken.IsCancellationRequested) - { - asyncReceiveFromToken.CompletionSource.SetCanceled(); - } - else - { - if (args.SocketError != SocketError.Success) - { - asyncReceiveFromToken.CompletionSource.SetException( - new SocketException((int)args.SocketError)); - } - else if (args.BytesTransferred <= 0) - { - TransmissionResult result = new TransmissionResult(args); - - asyncReceiveFromToken.CompletionSource.SetResult(result); - } - else - { - args.MemoryBuffer.CopyTo(asyncReceiveFromToken.UserBuffer); - - TransmissionResult result = new TransmissionResult(args); - - asyncReceiveFromToken.CompletionSource.SetResult(result); - } - } - - receiveFromBufferPool.Return(asyncReceiveFromToken.RentedBuffer, true); - receiveArgsPool.Return(args); - - break; - - default: - throw new InvalidOperationException( - $"The {nameof(Connection)} class doesn't support the {args.LastOperation} operation."); - } - } - - private readonly struct AsyncReadToken - { - public readonly CancellationToken CancellationToken; - public readonly TaskCompletionSource<TransmissionResult> CompletionSource; - public readonly byte[] RentedBuffer; - public readonly Memory<byte> UserBuffer; - - public AsyncReadToken(byte[] rentedBuffer, Memory<byte> userBuffer, TaskCompletionSource<TransmissionResult> tcs, - CancellationToken cancellationToken = default) - { - RentedBuffer = rentedBuffer; - UserBuffer = userBuffer; - - CompletionSource = tcs; - CancellationToken = cancellationToken; - } - } - - private readonly struct AsyncWriteToken - { - public readonly CancellationToken CancellationToken; - public readonly TaskCompletionSource<int> CompletionSource; - public readonly byte[] RentedBuffer; - - public AsyncWriteToken(byte[] rentedBuffer, TaskCompletionSource<int> tcs, - CancellationToken cancellationToken = default) - { - RentedBuffer = rentedBuffer; - - CompletionSource = tcs; - CancellationToken = cancellationToken; - } - } - - /// <summary> - /// The maximum number of stream connection that will be accepted. - /// </summary> - /// TODO change this to a configurable builder option - public const int MaximumConnectionBacklog = 10; - - /// <summary> - /// The maximum number of packets that will be stored before older packets start to be dropped. - /// </summary> - /// TODO change this to a configurable builder option - public const int MaximumPacketBacklog = 64; - - /// <inheritdoc /> - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - public Task<TransmissionResult> ReceiveAsync(Memory<byte> inputBuffer, SocketFlags flags, TimeSpan timeout) - { - using CancellationTokenSource timeoutCancellationTokenSource = new CancellationTokenSource(timeout); - using CancellationTokenSource cts = - CancellationTokenSource.CreateLinkedTokenSource(timeoutCancellationTokenSource.Token, ServerShutdownToken); - - return DoReceiveFromAsync(streamSocket, streamSocket.RemoteEndPoint, flags, inputBuffer, cts.Token); - } - - public Task<TransmissionResult> ReceiveFromAsync(EndPoint remoteEndPoint, Memory<byte> inputBuffer, SocketFlags flags, TimeSpan timeout) - { - using CancellationTokenSource timeoutCancellationTokenSource = new CancellationTokenSource(timeout); - using CancellationTokenSource cts = - CancellationTokenSource.CreateLinkedTokenSource(timeoutCancellationTokenSource.Token, ServerShutdownToken); - - return DoReceiveFromAsync(datagramSocket, remoteEndPoint, flags, inputBuffer, cts.Token); - } - - public ValueTask<int> SendAsync(Memory<byte> outputBuffer, SocketFlags flags, TimeSpan timeout) - { - using CancellationTokenSource timeoutCancellationTokenSource = new CancellationTokenSource(timeout); - using CancellationTokenSource cts = - CancellationTokenSource.CreateLinkedTokenSource(timeoutCancellationTokenSource.Token, ServerShutdownToken); - - return DoSendToAsync(streamSocket, streamSocket.RemoteEndPoint, flags, outputBuffer, cts.Token); - } - - public ValueTask<int> SendToAsync(EndPoint remoteEndPoint, Memory<byte> outputBuffer, SocketFlags flags, TimeSpan timeout) - { - using CancellationTokenSource timeoutCancellationTokenSource = new CancellationTokenSource(timeout); - using CancellationTokenSource cts = - CancellationTokenSource.CreateLinkedTokenSource(timeoutCancellationTokenSource.Token, ServerShutdownToken); - - return DoSendToAsync(datagramSocket, remoteEndPoint, flags, outputBuffer, cts.Token); - } - - /// <summary> - /// Configures the logger to log messages to the given stream (or to <see cref="Stream.Null"/> if <c>null</c>) and - /// to only log messages that are of severity <paramref name="minimumLoggedSeverity"/> or higher. - /// </summary> - /// <param name="loggingStream">The stream to which messages will be logged.</param> - /// <param name="minimumLoggedSeverity">The minimum severity a message must be to be logged.</param> - public void SetLoggingStream(Stream? loggingStream, LogLevel minimumLoggedSeverity = LogLevel.Info) - { - lock (loggerLockObject) - { - logger = new Logger(loggingStream ?? Stream.Null, minimumLoggedSeverity); - } - } - - /// <summary> - /// Attempts to asynchronously bind the underlying socket to the given local endpoint. Does not block. - /// If the timeout is exceeded the binding attempt is aborted and the method returns false. - /// </summary> - /// <param name="localEndPoint">The local endpoint to bind to.</param> - /// <param name="timeout">The timeout within which to attempt the binding.</param> - /// <returns>Whether the binding was successful or not.</returns> - public async Task<bool> TryBindAsync(EndPoint localEndPoint, TimeSpan timeout) - { - using CancellationTokenSource timeoutCancellationTokenSource = new CancellationTokenSource(timeout); - using CancellationTokenSource cts = - CancellationTokenSource.CreateLinkedTokenSource(timeoutCancellationTokenSource.Token, ServerShutdownToken); - - try - { - return await Task.Run(() => - { - streamSocket.Bind(localEndPoint); - datagramSocket.Bind(localEndPoint); - - return true; - }, cts.Token); - } - catch (TaskCanceledException) - { - return false; - } - catch (SocketException ex) - { - logger.LogException($"Socket exception on binding socket to {localEndPoint}:", ex); - return false; - } - } - - public async Task<bool> TryConnectAsync(EndPoint remoteEndPoint, TimeSpan timeout) - { - using CancellationTokenSource timeoutCancellationTokenSource = new CancellationTokenSource(timeout); - using CancellationTokenSource cts = - CancellationTokenSource.CreateLinkedTokenSource(timeoutCancellationTokenSource.Token, ServerShutdownToken); - - try - { - await DoConnectAsync(streamSocket, remoteEndPoint, cts.Token); - - return true; - } - catch (TaskCanceledException) - { - return false; - } - catch (SocketException ex) - { - logger.LogException($"Socket exception on connecting socket to {remoteEndPoint}:", ex); - return false; - } - } - - public async Task<bool> TryDisconnectAsync(TimeSpan timeout) - { - using CancellationTokenSource timeoutCancellationTokenSource = new CancellationTokenSource(timeout); - using CancellationTokenSource cts = - CancellationTokenSource.CreateLinkedTokenSource(timeoutCancellationTokenSource.Token, ServerShutdownToken); - - try - { - await DoDisconnectAsync(streamSocket, cts.Token); - - streamSocket.Shutdown(SocketShutdown.Both); - streamSocket.Close(1); - - return true; - } - catch (TaskCanceledException) - { - return false; - } - catch (SocketException ex) - { - logger.LogException($"Socket exception on disconnecting socket:", ex); - return false; - } - } - } -} -\ No newline at end of file diff --git a/NetSharp/NetSharp/ConnectionBuilder.cs b/NetSharp/NetSharp/ConnectionBuilder.cs @@ -1,191 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Net.Sockets; -using System.Security.Cryptography; -using NetSharp.Logging; -using NetSharp.Packets; -using NetSharp.Pipelines; - -namespace NetSharp -{ - /// <summary> - /// Allows for configuring and subsequently building a <see cref="Connection"/> instance. - /// </summary> - public sealed class ConnectionBuilder - { - private static readonly LoggingSettings DefaultLoggingSettings = - new LoggingSettings(Stream.Null, LogLevel.Warn); - - private static readonly PoolingSettings DefaultPoolingSettings = - new PoolingSettings(10, false); - - private readonly List<Func<Memory<byte>, Memory<byte>>> incomingPipelineStages = - new List<Func<Memory<byte>, Memory<byte>>>(); - - private readonly List<Func<Memory<byte>, Memory<byte>>> outgoingPipelineStages = - new List<Func<Memory<byte>, Memory<byte>>>(); - - private LoggingSettings? loggingSettings; - private PoolingSettings? poolingSettings; - - /// <summary> - /// The number of stages in the currently configured incoming packet pipeline. - /// </summary> - public int IncomingPacketPipelineStageCount - { - get { return incomingPipelineStages.Count; } - } - - /// <summary> - /// The number of stages in the currently configured outgoing packet pipeline. - /// </summary> - public int OutgoingPacketPipelineStageCount - { - get { return outgoingPipelineStages.Count; } - } - - /// <summary> - /// Returns a new <see cref="Connection"/> instance with the current configuration. - /// </summary> - /// <returns>The configured <see cref="Connection"/> instance.</returns> - public Connection Build() - { - PacketPipelineBuilder<Memory<byte>, Memory<byte>, NetworkPacket> incomingPipelineBuilder = - new PacketPipelineBuilder<Memory<byte>, Memory<byte>, NetworkPacket>(); - - incomingPipelineBuilder.WithInputStage(memory => memory); - foreach (Func<Memory<byte>, Memory<byte>> stage in incomingPipelineStages) - { - incomingPipelineBuilder = incomingPipelineBuilder.WithIntermediateStage(stage); - } - - incomingPipelineBuilder.WithOutputStage(NetworkPacket.Deserialise); - - PacketPipelineBuilder<NetworkPacket, Memory<byte>, Memory<byte>> outgoingPipelineBuilder = - new PacketPipelineBuilder<NetworkPacket, Memory<byte>, Memory<byte>>(); - - outgoingPipelineBuilder.WithInputStage(NetworkPacket.Serialise); - foreach (Func<Memory<byte>, Memory<byte>> stage in outgoingPipelineStages) - { - outgoingPipelineBuilder = outgoingPipelineBuilder.WithIntermediateStage(stage); - } - - outgoingPipelineBuilder.WithOutputStage(memory => memory); - - Connection connection = new Connection( - incomingPipelineBuilder.Build(), - outgoingPipelineBuilder.Build(), - poolingSettings?.ObjectPoolSize ?? DefaultPoolingSettings.ObjectPoolSize, - poolingSettings?.PreallocateBuffers ?? DefaultPoolingSettings.PreallocateBuffers, - loggingSettings?.LoggingStream ?? DefaultLoggingSettings.LoggingStream, - loggingSettings?.MinimumLevel ?? DefaultLoggingSettings.MinimumLevel); - - return connection; - } - - /// <summary> - /// Adds an extra pipeline stage to the currently configured incoming packet pipeline, at the given index. - /// </summary> - /// <param name="transform"> - /// The transformation that should be applied when a packet passes through the pipeline. - /// </param> - /// <param name="index">The position in the pipeline at which to place the transform.</param> - /// <returns>The builder instance for further configuration.</returns> - public ConnectionBuilder WithIncomingPipelineStage(in Func<Memory<byte>, Memory<byte>> transform, int index) - { - incomingPipelineStages.Insert(index, transform); - return this; - } - - /// <summary> - /// Sets the logging settings for the currently configured connection. - /// </summary> - /// <param name="settings">The logging settings to use.</param> - /// <returns>The builder instance for further configuration.</returns> - public ConnectionBuilder WithLogging(LoggingSettings settings) - { - loggingSettings = settings; - return this; - } - - /// <summary> - /// Adds an extra pipeline stage to the currently configured outgoing packet pipeline, at the given index. - /// </summary> - /// <param name="transform"> - /// The transformation that should be applied when a packet passes through the pipeline. - /// </param> - /// <param name="index">The position in the pipeline at which to place the transform.</param> - /// <returns>The builder instance for further configuration.</returns> - public ConnectionBuilder WithOutgoingPipelineStage(in Func<Memory<byte>, Memory<byte>> transform, int index) - { - outgoingPipelineStages.Insert(index, transform); - return this; - } - - /// <summary> - /// Sets the pooling settings for the currently configured connection. - /// </summary> - /// <param name="settings">The pooling settings to use.</param> - /// <returns>The builder instance for further configuration.</returns> - public ConnectionBuilder WithPooling(PoolingSettings settings) - { - poolingSettings = settings; - return this; - } - - /// <summary> - /// Holds settings for configuring a connection's logging. - /// </summary> - public readonly struct LoggingSettings - { - /// <summary> - /// The stream to which messages will be logged. - /// </summary> - public readonly Stream LoggingStream; - - /// <summary> - /// The minimum severity that a log message must have to be recorded. - /// </summary> - public readonly LogLevel MinimumLevel; - - /// <summary> - /// Initialises a new instance of the <see cref="LoggingStream"/> struct. - /// </summary> - /// <param name="stream">The stream to which messages will be logged..</param> - /// <param name="minimumLevel">The minimum severity that a log message must have to be recorded.</param> - public LoggingSettings(Stream stream, LogLevel minimumLevel) - { - LoggingStream = stream; - MinimumLevel = minimumLevel; - } - } - - /// <summary> - /// Holds settings for configuring a connection's buffer pooling. - /// </summary> - public readonly struct PoolingSettings - { - /// <summary> - /// The number of objects that will be held in the object pools. - /// </summary> - public readonly int ObjectPoolSize; - - /// <summary> - /// Whether the buffers for receiving messages should be preallocated. - /// </summary> - public readonly bool PreallocateBuffers; - - /// <summary> - /// Initialises a new instance of the <see cref="PoolingSettings"/> struct. - /// </summary> - /// <param name="poolSize">The number of objects that will be held in the object pools.</param> - /// <param name="preallocateBuffers">Whether the buffers for receiving messages should be preallocated.</param> - public PoolingSettings(int poolSize, bool preallocateBuffers) - { - ObjectPoolSize = poolSize; - PreallocateBuffers = preallocateBuffers; - } - } - } -} -\ No newline at end of file diff --git a/NetSharp/NetSharp/Deprecated/Builtin/ConnectPacket.cs b/NetSharp/NetSharp/Deprecated/Builtin/ConnectPacket.cs @@ -0,0 +1,32 @@ +using System; + +namespace NetSharp.Deprecated.Builtin +{ + /// <summary> + /// A simple connection request packet for the UDP protocol. + /// </summary> + [PacketTypeId(1)] + internal class ConnectPacket : IRequestPacket + { + /// <inheritdoc /> + public void AfterDeserialisation() + { + } + + /// <inheritdoc /> + public void BeforeSerialisation() + { + } + + /// <inheritdoc /> + public void Deserialise(ReadOnlyMemory<byte> serialisedObject) + { + } + + /// <inheritdoc /> + public Memory<byte> Serialise() + { + return Memory<byte>.Empty; + } + } +} +\ No newline at end of file diff --git a/NetSharp/NetSharp/Deprecated/Builtin/ConnectResponsePacket.cs b/NetSharp/NetSharp/Deprecated/Builtin/ConnectResponsePacket.cs @@ -0,0 +1,35 @@ +using System; + +namespace NetSharp.Deprecated.Builtin +{ + /// <summary> + /// A response packet for the <see cref="ConnectPacket"/>. + /// </summary> + [PacketTypeId(2)] + internal class ConnectResponsePacket : IResponsePacket<ConnectPacket> + { + /// <inheritdoc /> + public ConnectPacket RequestPacket { get; set; } = new ConnectPacket(); + + /// <inheritdoc /> + public void AfterDeserialisation() + { + } + + /// <inheritdoc /> + public void BeforeSerialisation() + { + } + + /// <inheritdoc /> + public void Deserialise(ReadOnlyMemory<byte> serialisedObject) + { + } + + /// <inheritdoc /> + public Memory<byte> Serialise() + { + return Memory<byte>.Empty; + } + } +} +\ No newline at end of file diff --git a/NetSharp/NetSharp/Deprecated/Builtin/DataPacket.cs b/NetSharp/NetSharp/Deprecated/Builtin/DataPacket.cs @@ -0,0 +1,55 @@ +using System; + +namespace NetSharp.Deprecated.Builtin +{ + /// <summary> + /// A simple data transfer packet, that allows for the transmission of an arbitrary number of frames. + /// </summary> + [PacketTypeId(5)] + public class DataPacket : IRequestPacket + { + /// <summary> + /// The data that should be transferred across the network. + /// </summary> + public Memory<byte> RequestBuffer; + + /// <summary> + /// Initialises a new instance of the <see cref="DataPacket"/> class. + /// </summary> + public DataPacket() + { + RequestBuffer = new byte[0]; + } + + /// <summary> + /// Initialises a new instance of the <see cref="DataPacket"/> class. + /// </summary> + /// <param name="buffer">The data that this request packet should contain.</param> + public DataPacket(Memory<byte> buffer) + { + RequestBuffer = buffer; + } + + /// <inheritdoc /> + public void AfterDeserialisation() + { + } + + /// <inheritdoc /> + public void BeforeSerialisation() + { + } + + /// <inheritdoc /> + public void Deserialise(ReadOnlyMemory<byte> serialisedObject) + { + RequestBuffer = serialisedObject.ToArray(); + } + + /// <inheritdoc /> + public Memory<byte> Serialise() + { + return RequestBuffer; + } + } +} +\ No newline at end of file diff --git a/NetSharp/NetSharp/Deprecated/Builtin/DataResponsePacket.cs b/NetSharp/NetSharp/Deprecated/Builtin/DataResponsePacket.cs @@ -0,0 +1,58 @@ +using System; + +namespace NetSharp.Deprecated.Builtin +{ + /// <summary> + /// A response packet for the <see cref="DataPacket"/>. + /// </summary> + [PacketTypeId(6)] + public class DataResponsePacket : IResponsePacket<DataPacket> + { + /// <summary> + /// The data that should be transferred across the network. + /// </summary> + public Memory<byte> ResponseBuffer; + + /// <summary> + /// Initialises a new instance of the <see cref="DataResponsePacket"/> class. + /// </summary> + public DataResponsePacket() + { + ResponseBuffer = new byte[0]; + } + + /// <summary> + /// Initialises a new instance of the <see cref="DataResponsePacket"/> class. + /// </summary> + /// <param name="buffer">The data that this response packet should contain.</param> + public DataResponsePacket(Memory<byte> buffer) + { + ResponseBuffer = buffer; + } + + /// <inheritdoc /> + public DataPacket RequestPacket { get; internal set; } = new DataPacket(); + + /// <inheritdoc /> + public void AfterDeserialisation() + { + } + + /// <inheritdoc /> + public void BeforeSerialisation() + { + } + + /// <inheritdoc /> + public void Deserialise(ReadOnlyMemory<byte> serialisedObject) + { + ResponseBuffer = serialisedObject.ToArray(); + } + + /// <inheritdoc /> + public Memory<byte> Serialise() + { + return ResponseBuffer; + } + } +} +\ No newline at end of file diff --git a/NetSharp/NetSharp/Deprecated/Builtin/DisconnectPacket.cs b/NetSharp/NetSharp/Deprecated/Builtin/DisconnectPacket.cs @@ -0,0 +1,32 @@ +using System; + +namespace NetSharp.Deprecated.Builtin +{ + /// <summary> + /// A simple disconnect packet for the UDP protocol. + /// </summary> + [PacketTypeId(0)] + internal class DisconnectPacket : IRequestPacket + { + /// <inheritdoc /> + public void AfterDeserialisation() + { + } + + /// <inheritdoc /> + public void BeforeSerialisation() + { + } + + /// <inheritdoc /> + public void Deserialise(ReadOnlyMemory<byte> serialisedObject) + { + } + + /// <inheritdoc /> + public Memory<byte> Serialise() + { + return Memory<byte>.Empty; + } + } +} +\ No newline at end of file diff --git a/NetSharp/NetSharp/Deprecated/Builtin/PingPacket.cs b/NetSharp/NetSharp/Deprecated/Builtin/PingPacket.cs @@ -0,0 +1,32 @@ +using System; + +namespace NetSharp.Deprecated.Builtin +{ + /// <summary> + /// A simple ping request packet for heartbeat monitoring and RTT measurement. + /// </summary> + [PacketTypeId(3)] + public class PingPacket : IRequestPacket + { + /// <inheritdoc /> + public void AfterDeserialisation() + { + } + + /// <inheritdoc /> + public void BeforeSerialisation() + { + } + + /// <inheritdoc /> + public void Deserialise(ReadOnlyMemory<byte> serialisedObject) + { + } + + /// <inheritdoc /> + public Memory<byte> Serialise() + { + return Memory<byte>.Empty; + } + } +} +\ No newline at end of file diff --git a/NetSharp/NetSharp/Deprecated/Builtin/PingResponsePacket.cs b/NetSharp/NetSharp/Deprecated/Builtin/PingResponsePacket.cs @@ -0,0 +1,35 @@ +using System; + +namespace NetSharp.Deprecated.Builtin +{ + /// <summary> + /// A response packet for the <see cref="PingPacket"/>. + /// </summary> + [PacketTypeId(4)] + public class PingResponsePacket : IResponsePacket<PingPacket> + { + /// <inheritdoc /> + public PingPacket RequestPacket { get; internal set; } = new PingPacket(); + + /// <inheritdoc /> + public void AfterDeserialisation() + { + } + + /// <inheritdoc /> + public void BeforeSerialisation() + { + } + + /// <inheritdoc /> + public void Deserialise(ReadOnlyMemory<byte> serialisedObject) + { + } + + /// <inheritdoc /> + public Memory<byte> Serialise() + { + return Memory<byte>.Empty; + } + } +} +\ No newline at end of file diff --git a/NetSharp/NetSharp/Deprecated/Builtin/SimpleDataPacket.cs b/NetSharp/NetSharp/Deprecated/Builtin/SimpleDataPacket.cs @@ -0,0 +1,55 @@ +using System; + +namespace NetSharp.Deprecated.Builtin +{ + /// <summary> + /// A simple one-time-use data transfer packet, that allows for the transmission of an arbitrary number of frames. + /// </summary> + [PacketTypeId(7)] + public class SimpleDataPacket : IRequestPacket + { + /// <summary> + /// The data that should be transferred across the network. + /// </summary> + public Memory<byte> RequestBuffer; + + /// <summary> + /// Initialises a new instance of the <see cref="SimpleDataPacket"/> class. + /// </summary> + public SimpleDataPacket() + { + RequestBuffer = new byte[0]; + } + + /// <summary> + /// Initialises a new instance of the <see cref="SimpleDataPacket"/> class. + /// </summary> + /// <param name="buffer">The data that this request packet should contain.</param> + public SimpleDataPacket(Memory<byte> buffer) + { + RequestBuffer = buffer; + } + + /// <inheritdoc /> + public void AfterDeserialisation() + { + } + + /// <inheritdoc /> + public void BeforeSerialisation() + { + } + + /// <inheritdoc /> + public void Deserialise(ReadOnlyMemory<byte> serialisedObject) + { + RequestBuffer = serialisedObject.ToArray(); + } + + /// <inheritdoc /> + public Memory<byte> Serialise() + { + return RequestBuffer; + } + } +} +\ No newline at end of file diff --git a/NetSharp/NetSharp/Deprecated/Client.cs b/NetSharp/NetSharp/Deprecated/Client.cs @@ -1,10 +1,11 @@ -using System; +using NetSharp.Deprecated.Builtin; + +using System; using System.Net; using System.Net.Sockets; using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; -using NetSharp.Packets.Builtin; namespace NetSharp.Deprecated { diff --git a/NetSharp/NetSharp/Deprecated/Connection.cs b/NetSharp/NetSharp/Deprecated/Connection.cs @@ -0,0 +1,353 @@ +using Microsoft.Extensions.ObjectPool; + +using NetSharp.Utils; + +using System; +using System.Buffers; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.IO; +using System.Net; +using System.Net.Sockets; +using System.Threading; +using System.Threading.Channels; +using System.Threading.Tasks; + +namespace NetSharp.Deprecated +{ + /// <summary> + /// Encapsulates a connection capable of receiving packets and responding to them with registered packet handlers. + /// </summary> + public sealed partial class Connection : IDisposable + { + private readonly HashSet<EndPoint> datagramConnections; + private readonly Channel<(EndPoint origin, Memory<byte> packet)> incomingPacketChannel; + + /// <summary> + /// Pipeline to convert incoming byte buffers to <see cref="NetworkPacket"/> instances. + /// </summary> + private readonly PacketPipeline<Memory<byte>, Memory<byte>, NetworkPacket> incomingPacketPipeline; + + /// <summary> + /// Lock synchronisation object for the <see cref="logger"/> variable. + /// </summary> + private readonly object loggerLockObject = new object(); + + private readonly Channel<(EndPoint destination, NetworkPacket packet)> outgoingPacketChannel; + + /// <summary> + /// Pipeline to convert outgoing <see cref="NetworkPacket"/> instances to a byte buffer for sending. + /// </summary> + private readonly PacketPipeline<NetworkPacket, Memory<byte>, Memory<byte>> outgoingPacketPipeline; + + private readonly Channel<(EndPoint origin, IRequestPacket request)> requestChannel; + + /// <summary> + /// Cancellation token which allows observing the shutdown of the server. It is set when <see cref="ShutdownServer"/> is called. + /// </summary> + private readonly CancellationToken ServerShutdownToken; + + private readonly ConcurrentDictionary<EndPoint, Socket> streamConnections; + + /// <summary> + /// A logger object allowing for writing debug messages to an output stream. + /// </summary> + private Logger logger; + + /// <summary> + /// Destroys a <see cref="Connection"/> class instance, freeing all managed resources. + /// </summary> + ~Connection() + { + Dispose(false); + } + + private async Task AcceptorWork(object shutdownToken) + { + CancellationToken cancellationToken = (CancellationToken)shutdownToken; + + logger.LogMessage("Started stream acceptor task."); + + async Task StreamListenerWork(object clientArgsObj) + { + Socket clientSocket = (Socket)clientArgsObj; + EndPoint clientEndPoint = clientSocket.RemoteEndPoint; + + logger.LogMessage($"Client handler started for {clientEndPoint}"); + + while (!cancellationToken.IsCancellationRequested) + { + // TODO: implement receive buffer pooling + byte[] receiveBuffer = new byte[NetworkPacket.PacketSize]; + Memory<byte> receiveBufferMemory = new Memory<byte>(receiveBuffer); + + TransmissionResult result = + await DoReceiveFromAsync(clientSocket, clientEndPoint, SocketFlags.None, receiveBufferMemory, cancellationToken); + + if (result.Count == 0) + { + break; + } + + await incomingPacketChannel.Writer.WriteAsync((result.RemoteEndPoint, receiveBufferMemory), + cancellationToken); + } + + logger.LogMessage($"Client handler stopped for {clientEndPoint}"); + + await DoDisconnectAsync(clientSocket, cancellationToken); + + clientSocket.Shutdown(SocketShutdown.Both); + clientSocket.Close(1); + } + + streamSocket.Listen(MaximumConnectionBacklog); + + while (!cancellationToken.IsCancellationRequested) + { + Socket clientSocket = await DoAcceptAsync(streamSocket, cancellationToken); + + if (!streamConnections.ContainsKey(clientSocket.RemoteEndPoint)) + { + streamConnections[clientSocket.RemoteEndPoint] = clientSocket; + + await Task.Factory.StartNew(StreamListenerWork, streamConnections[clientSocket.RemoteEndPoint], ServerShutdownToken); + } + else + { + logger.LogWarning($"Accepted duplicate connection from {clientSocket.RemoteEndPoint}"); + } + } + + logger.LogMessage("Stopped stream acceptor task."); + } + + private async Task DatagramListenerWork(object shutdownToken) + { + CancellationToken cancellationToken = (CancellationToken)shutdownToken; + + logger.LogMessage("Started datagram listener task."); + + while (!cancellationToken.IsCancellationRequested) + { + // TODO: implement receive buffer pooling + byte[] receiveBuffer = new byte[NetworkPacket.PacketSize]; + Memory<byte> receiveBufferMemory = new Memory<byte>(receiveBuffer); + + TransmissionResult result = + await DoReceiveFromAsync(datagramSocket, AnyRemoteEndPoint, SocketFlags.None, + receiveBufferMemory, cancellationToken); + + if (!datagramConnections.Contains(result.RemoteEndPoint)) + { + datagramConnections.Add(result.RemoteEndPoint); + } + + await incomingPacketChannel.Writer.WriteAsync((result.RemoteEndPoint, receiveBufferMemory), cancellationToken); + } + + logger.LogMessage("Stopped datagram listener task."); + } + + private async Task IncomingPacketHandlerWork(object shutdownToken) + { + CancellationToken cancellationToken = (CancellationToken)shutdownToken; + + logger.LogMessage("Started incoming packet handler task."); + + while (!cancellationToken.IsCancellationRequested) + { + (EndPoint origin, Memory<byte> packet) = + await incomingPacketChannel.Reader.ReadAsync(cancellationToken); + + NetworkPacket deserialisedRequest = incomingPacketPipeline.ProcessPacket(packet); + + // TODO implement deserialisation according to registered packet deserialisers + + // TODO: write to requestChannel, not to outgoingPacketChannel + await outgoingPacketChannel.Writer.WriteAsync((origin, deserialisedRequest), cancellationToken); + } + + logger.LogMessage("Stopped incoming packet handler task."); + } + + private async Task OutgoingPacketHandlerWork(object shutdownToken) + { + CancellationToken cancellationToken = (CancellationToken)shutdownToken; + + logger.LogMessage("Started outgoing packet handler task."); + + while (!cancellationToken.IsCancellationRequested) + { + (EndPoint destination, NetworkPacket packet) = + await outgoingPacketChannel.Reader.ReadAsync(cancellationToken); + + Memory<byte> serialisedResponse = outgoingPacketPipeline.ProcessPacket(packet); + + if (streamConnections.ContainsKey(destination)) + { + Socket streamConnection = streamConnections[destination]; + + await DoSendToAsync(streamConnection, destination, SocketFlags.None, + serialisedResponse, cancellationToken); + } + else if (datagramConnections.Contains(destination)) + { + await DoSendToAsync(datagramSocket, destination, SocketFlags.None, + serialisedResponse, cancellationToken); + } + else + { + logger.LogWarning($"Packet destined for unknown destination: {destination}"); + } + } + + logger.LogMessage("Stopped outgoing packet handler task."); + } + + private async Task RequestHandlerInvocationWork(object shutdownToken) + { + CancellationToken cancellationToken = (CancellationToken)shutdownToken; + + logger.LogMessage("Started request handler invocation task."); + + while (!cancellationToken.IsCancellationRequested) + { + (EndPoint origin, IRequestPacket request) = await requestChannel.Reader.ReadAsync(cancellationToken); + + // TODO: implement proper request handling, and conversion to IResponsePacket<IRequestPacke> + + NetworkPacket serialisedResponsePacket = new NetworkPacket(); + + await outgoingPacketChannel.Writer.WriteAsync((origin, serialisedResponsePacket), cancellationToken); + } + + logger.LogMessage("Stopped request handler invocation task."); + } + + internal Connection( + PacketPipeline<Memory<byte>, Memory<byte>, NetworkPacket> incomingPacketPipeline, + PacketPipeline<NetworkPacket, Memory<byte>, Memory<byte>> outgoingPacketPipeline, + int objectPoolSize = 10, bool preallocateBuffers = false, Stream? loggingStream = default, + LogLevel minimumLoggedSeverity = LogLevel.Info) + { + serverShutdownTokenSource = new CancellationTokenSource(); + ServerShutdownToken = serverShutdownTokenSource.Token; + + streamSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + datagramSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); + + sendToBufferPool = ArrayPool<byte>.Create(NetworkPacket.PacketSize, objectPoolSize); + receiveFromBufferPool = ArrayPool<byte>.Create(NetworkPacket.PacketSize, objectPoolSize); + + clientSocketArgsPool = + new LeakTrackingObjectPool<SocketAsyncEventArgs>( + new DefaultObjectPool<SocketAsyncEventArgs>(new DefaultPooledObjectPolicy<SocketAsyncEventArgs>(), + objectPoolSize)); + receiveArgsPool = + new LeakTrackingObjectPool<SocketAsyncEventArgs>( + new DefaultObjectPool<SocketAsyncEventArgs>(new DefaultPooledObjectPolicy<SocketAsyncEventArgs>(), + objectPoolSize)); + sendArgsPool = + new LeakTrackingObjectPool<SocketAsyncEventArgs>( + new DefaultObjectPool<SocketAsyncEventArgs>(new DefaultPooledObjectPolicy<SocketAsyncEventArgs>(), + objectPoolSize)); + + for (int i = 0; i < objectPoolSize; i++) + { + SocketAsyncEventArgs clientArgs = new SocketAsyncEventArgs(); + clientArgs.Completed += HandleIOCompleted; + clientSocketArgsPool.Return(clientArgs); + + SocketAsyncEventArgs receiveArgs = new SocketAsyncEventArgs(); + receiveArgs.Completed += HandleIOCompleted; + receiveArgsPool.Return(receiveArgs); + + SocketAsyncEventArgs sendArgs = new SocketAsyncEventArgs(); + sendArgs.Completed += HandleIOCompleted; + sendArgsPool.Return(sendArgs); + } + + if (preallocateBuffers) + { + //TODO: Preallocate buffers someday + } + + streamConnections = new ConcurrentDictionary<EndPoint, Socket>(); + datagramConnections = new HashSet<EndPoint>(); + + this.incomingPacketPipeline = incomingPacketPipeline; + BoundedChannelOptions incomingChannelOptions = new BoundedChannelOptions(MaximumPacketBacklog) + { + FullMode = BoundedChannelFullMode.DropOldest, + SingleReader = true, + SingleWriter = false, + }; + incomingPacketChannel = Channel.CreateBounded<(EndPoint origin, Memory<byte> packet)>(incomingChannelOptions); + + BoundedChannelOptions requestChannelOptions = new BoundedChannelOptions(MaximumPacketBacklog) + { + FullMode = BoundedChannelFullMode.DropOldest, + SingleReader = true, + SingleWriter = true, + }; + requestChannel = Channel.CreateBounded<(EndPoint origin, IRequestPacket request)>(requestChannelOptions); + + this.outgoingPacketPipeline = outgoingPacketPipeline; + BoundedChannelOptions outgoingChannelOptions = new BoundedChannelOptions(MaximumPacketBacklog) + { + FullMode = BoundedChannelFullMode.DropOldest, + SingleReader = true, + SingleWriter = true, + }; + outgoingPacketChannel = Channel.CreateBounded<(EndPoint destination, NetworkPacket packet)>(outgoingChannelOptions); + + logger = new Logger(loggingStream ?? Stream.Null, minimumLoggedSeverity); + } + + /// <summary> + /// Makes the connection listen for incoming request packets, and handle them according to registered packet handler delegates. + /// This work can be cancelled by calling <see cref="ShutdownServer"/>. + /// </summary> + /// <returns>The task representing the connection work.</returns> + public Task RunServerAsync() + { + logger.LogMessage("Starting stream acceptor task..."); + Task acceptorThread = + Task.Factory.StartNew(AcceptorWork, ServerShutdownToken, ServerShutdownToken, TaskCreationOptions.LongRunning, + TaskScheduler.Default).Result; + + logger.LogMessage("Starting datagram listener task..."); + Task datagramListenerThread = + Task.Factory.StartNew(DatagramListenerWork, ServerShutdownToken, ServerShutdownToken, TaskCreationOptions.LongRunning, + TaskScheduler.Default); + + logger.LogMessage("Starting incoming packet handler task..."); + Task incomingPacketHandlerThread = + Task.Factory.StartNew(IncomingPacketHandlerWork, ServerShutdownToken, ServerShutdownToken, TaskCreationOptions.LongRunning, + TaskScheduler.Default); + + logger.LogMessage("Starting request packet invocation task..."); + Task requestHandlerInvocationThread = + Task.Factory.StartNew(RequestHandlerInvocationWork, ServerShutdownToken, ServerShutdownToken, TaskCreationOptions.LongRunning, + TaskScheduler.Default).Result; + + logger.LogMessage("Starting outgoing packet handler task..."); + Task outgoingPacketHandlerThread = + Task.Factory.StartNew(OutgoingPacketHandlerWork, ServerShutdownToken, ServerShutdownToken, TaskCreationOptions.LongRunning, + TaskScheduler.Default); + + return Task.WhenAll(acceptorThread, datagramListenerThread, + incomingPacketHandlerThread, requestHandlerInvocationThread, outgoingPacketHandlerThread); + } + + /// <summary> + /// Shuts down the connection, and releases managed and unmanaged resources. + /// </summary> + public void ShutdownServer() + { + logger.LogMessage("Signalling shutdown to all client connection handlers..."); + serverShutdownTokenSource.Cancel(); + } + } +} +\ No newline at end of file diff --git a/NetSharp/NetSharp/Deprecated/ConnectionBase.cs b/NetSharp/NetSharp/Deprecated/ConnectionBase.cs @@ -0,0 +1,465 @@ +using Microsoft.Extensions.ObjectPool; + +using NetSharp.Utils; + +using System; +using System.Buffers; +using System.IO; +using System.Net; +using System.Net.Sockets; +using System.Threading; +using System.Threading.Tasks; + +namespace NetSharp.Deprecated +{ + /// <summary> + /// Implements low-level network access on top of which the rest of the connection is built upon. + /// </summary> + public sealed partial class Connection : IDisposable + { + /// <summary> + /// Represents any remote endpoint for datagram operations. + /// </summary> + private static readonly EndPoint AnyRemoteEndPoint = new IPEndPoint(IPAddress.Any, 0); + + private readonly ObjectPool<SocketAsyncEventArgs> clientSocketArgsPool; + + private readonly Socket datagramSocket; + + private readonly ObjectPool<SocketAsyncEventArgs> receiveArgsPool; + + private readonly ArrayPool<byte> receiveFromBufferPool; + + private readonly ObjectPool<SocketAsyncEventArgs> sendArgsPool; + + private readonly ArrayPool<byte> sendToBufferPool; + + private readonly CancellationTokenSource serverShutdownTokenSource; + + private readonly Socket streamSocket; + + /// <summary> + /// Disposes of the managed and unmanaged resources held by this instance. + /// </summary> + /// <param name="disposing">Whether this method is called by <see cref="Dispose()"/> or by the finaliser.</param> + private void Dispose(bool disposing) + { + if (disposing) + { + serverShutdownTokenSource.Cancel(); + serverShutdownTokenSource.Dispose(); + + streamSocket.Dispose(); + datagramSocket.Dispose(); + } + } + + /// <summary> + /// Provides an awaitable wrapper around an asynchronous socket accept operation. + /// </summary> + /// <param name="serverSocket">The socket which should be used to accept an incoming connection attempt.</param> + /// <param name="cancellationToken">The cancellation token to observe for the operation.</param> + /// <returns>The accepted socket.</returns> + private async Task<Socket> DoAcceptAsync(Socket serverSocket, CancellationToken cancellationToken = default) + { + TaskCompletionSource<Socket> tcs = new TaskCompletionSource<Socket>(); + + cancellationToken.Register(() => tcs.SetCanceled()); + + Task<Socket> task = serverSocket.AcceptAsync(); + Task<Socket> completedTask = await Task.WhenAny(task, tcs.Task); + + if (completedTask == task) + { + Socket result = await task; + + tcs.SetResult(result); + } + + return await tcs.Task; + } + + /// <summary> + /// Provides an awaitable wrapper around an asynchronous socket connect operation. + /// </summary> + /// <param name="socket">The socket which should asynchronously connect to the remote endpoint.</param> + /// <param name="remoteEndPoint">The remote endpoint to which the socket should connect.</param> + /// <param name="cancellationToken">The cancellation token to observe for the operation.</param> + private async Task DoConnectAsync(Socket socket, EndPoint remoteEndPoint, CancellationToken cancellationToken = default) + { + TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>(); + + cancellationToken.Register(() => tcs.SetCanceled()); + + Task task = socket.ConnectAsync(remoteEndPoint); + Task completedTask = await Task.WhenAny(task, tcs.Task); + + if (completedTask == task) + { + await task; + tcs.SetResult(true); + } + + await tcs.Task; + } + + /// <summary> + /// Provides an awaitable wrapper around an asynchronous socket disconnect operation. + /// </summary> + /// <param name="connectedSocket">The socket which should asynchronously disconnect from its remote endpoint.</param> + /// <param name="cancellationToken">The cancellation token to observe for the operation.</param> + private Task DoDisconnectAsync(Socket connectedSocket, CancellationToken cancellationToken = default) + { + return Task.Factory.StartNew(() => + { + connectedSocket.Disconnect(true); + }, cancellationToken); + } + + /// <summary> + /// Provides an awaitable wrapper around an asynchronous socket receive operation. + /// </summary> + /// <param name="listenerSocket">The socket which should receive data from the remote endpoint.</param> + /// <param name="remoteEndPoint">The remove endpoint from which data should be received.</param> + /// <param name="socketFlags">The socket flags associated with the receive operation.</param> + /// <param name="inputBuffer">The memory buffer into which received data will be stored.</param> + /// <param name="cancellationToken">The cancellation token to observe for the operation.</param> + /// <returns>The result of the receive operation from the remote endpoint.</returns> + private Task<TransmissionResult> DoReceiveFromAsync(Socket listenerSocket, EndPoint remoteEndPoint, SocketFlags socketFlags, + Memory<byte> inputBuffer, CancellationToken cancellationToken = default) + { + TaskCompletionSource<TransmissionResult> tcs = new TaskCompletionSource<TransmissionResult>(); + + byte[] rentedReceiveFromBuffer = receiveFromBufferPool.Rent(NetworkPacket.PacketSize); + Memory<byte> rentedReceiveFromBufferMemory = new Memory<byte>(rentedReceiveFromBuffer); + + SocketAsyncEventArgs clientArgs = receiveArgsPool.Get(); + clientArgs.SetBuffer(rentedReceiveFromBufferMemory); + clientArgs.SocketFlags = socketFlags; + clientArgs.RemoteEndPoint = remoteEndPoint; + clientArgs.UserToken = new AsyncReadToken(rentedReceiveFromBuffer, inputBuffer, tcs, cancellationToken); + + // if the receive operation doesn't complete synchronously, returns the awaitable task + if (listenerSocket.ReceiveFromAsync(clientArgs)) return tcs.Task; + + clientArgs.MemoryBuffer.CopyTo(inputBuffer); + + TransmissionResult result = new TransmissionResult(clientArgs); + + receiveFromBufferPool.Return(rentedReceiveFromBuffer, true); + receiveArgsPool.Return(clientArgs); + + return Task.FromResult(result); + } + + /// <summary> + /// Provides an awaitable wrapper around an asynchronous socket send operation. + /// </summary> + /// <param name="transmitterSocket">The socket which should send the data to the remote endpoint.</param> + /// <param name="remoteEndPoint">The remote endpoint to which data should be written.</param> + /// <param name="socketFlags">The socket flags associated with the send operation.</param> + /// <param name="outputBuffer">The data buffer which should be sent.</param> + /// <param name="cancellationToken">The cancellation token to observe for the operation.</param> + /// <returns>The result of the send operation to the remote endpoint.</returns> + private ValueTask<int> DoSendToAsync(Socket transmitterSocket, EndPoint remoteEndPoint, SocketFlags socketFlags, + Memory<byte> outputBuffer, CancellationToken cancellationToken = default) + { + TaskCompletionSource<int> tcs = new TaskCompletionSource<int>(); + + byte[] rentedSendToBuffer = sendToBufferPool.Rent(NetworkPacket.PacketSize); + Memory<byte> rentedSendToBufferMemory = new Memory<byte>(rentedSendToBuffer); + + outputBuffer.CopyTo(rentedSendToBufferMemory); + + SocketAsyncEventArgs clientArgs = sendArgsPool.Get(); + clientArgs.SetBuffer(rentedSendToBufferMemory); + clientArgs.SocketFlags = socketFlags; + clientArgs.RemoteEndPoint = remoteEndPoint; + clientArgs.UserToken = new AsyncWriteToken(rentedSendToBuffer, tcs, cancellationToken); + + /* NOT WORKING, NEED SOLUTION AT SOME POINT!!! + // register cleanup action for when the cancellation token is thrown + cancellationToken.Register(() => + { + tcs.SetCanceled(); + + sendBufferPool.Return(rentedSendToBuffer, true); + + //TODO this is probably a hideous solution. find a better one + args.Completed -= HandleIOCompleted; + args.Dispose(); + + SocketAsyncEventArgs newArgs = new SocketAsyncEventArgs(); + newArgs.Completed += HandleIOCompleted; + sendAsyncEventArgsPool.Return(newArgs); + }); + */ + + // if the send operation doesn't complete synchronously, return the awaitable task + if (transmitterSocket.SendToAsync(clientArgs)) return new ValueTask<int>(tcs.Task); + + int result = clientArgs.BytesTransferred; + + sendToBufferPool.Return(rentedSendToBuffer, true); + sendArgsPool.Return(clientArgs); + + return new ValueTask<int>(result); + } + + private void HandleIOCompleted(object? sender, SocketAsyncEventArgs args) + { + switch (args.LastOperation) + { + case SocketAsyncOperation.SendTo: + AsyncWriteToken asyncSendToToken = (AsyncWriteToken)args.UserToken; + + if (asyncSendToToken.CancellationToken.IsCancellationRequested) + { + asyncSendToToken.CompletionSource.SetCanceled(); + } + else + { + if (args.SocketError != SocketError.Success) + { + asyncSendToToken.CompletionSource.SetException( + new SocketException((int)args.SocketError)); + } + else + { + asyncSendToToken.CompletionSource.SetResult(args.BytesTransferred); + } + } + + sendToBufferPool.Return(asyncSendToToken.RentedBuffer, true); + sendArgsPool.Return(args); + + break; + + case SocketAsyncOperation.ReceiveFrom: + AsyncReadToken asyncReceiveFromToken = (AsyncReadToken)args.UserToken; + + if (asyncReceiveFromToken.CancellationToken.IsCancellationRequested) + { + asyncReceiveFromToken.CompletionSource.SetCanceled(); + } + else + { + if (args.SocketError != SocketError.Success) + { + asyncReceiveFromToken.CompletionSource.SetException( + new SocketException((int)args.SocketError)); + } + else if (args.BytesTransferred <= 0) + { + TransmissionResult result = new TransmissionResult(args); + + asyncReceiveFromToken.CompletionSource.SetResult(result); + } + else + { + args.MemoryBuffer.CopyTo(asyncReceiveFromToken.UserBuffer); + + TransmissionResult result = new TransmissionResult(args); + + asyncReceiveFromToken.CompletionSource.SetResult(result); + } + } + + receiveFromBufferPool.Return(asyncReceiveFromToken.RentedBuffer, true); + receiveArgsPool.Return(args); + + break; + + default: + throw new InvalidOperationException( + $"The {nameof(Connection)} class doesn't support the {args.LastOperation} operation."); + } + } + + private readonly struct AsyncReadToken + { + public readonly CancellationToken CancellationToken; + public readonly TaskCompletionSource<TransmissionResult> CompletionSource; + public readonly byte[] RentedBuffer; + public readonly Memory<byte> UserBuffer; + + public AsyncReadToken(byte[] rentedBuffer, Memory<byte> userBuffer, TaskCompletionSource<TransmissionResult> tcs, + CancellationToken cancellationToken = default) + { + RentedBuffer = rentedBuffer; + UserBuffer = userBuffer; + + CompletionSource = tcs; + CancellationToken = cancellationToken; + } + } + + private readonly struct AsyncWriteToken + { + public readonly CancellationToken CancellationToken; + public readonly TaskCompletionSource<int> CompletionSource; + public readonly byte[] RentedBuffer; + + public AsyncWriteToken(byte[] rentedBuffer, TaskCompletionSource<int> tcs, + CancellationToken cancellationToken = default) + { + RentedBuffer = rentedBuffer; + + CompletionSource = tcs; + CancellationToken = cancellationToken; + } + } + + /// <summary> + /// The maximum number of stream connection that will be accepted. + /// </summary> + /// TODO change this to a configurable builder option + public const int MaximumConnectionBacklog = 10; + + /// <summary> + /// The maximum number of packets that will be stored before older packets start to be dropped. + /// </summary> + /// TODO change this to a configurable builder option + public const int MaximumPacketBacklog = 64; + + /// <inheritdoc /> + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + public Task<TransmissionResult> ReceiveAsync(Memory<byte> inputBuffer, SocketFlags flags, TimeSpan timeout) + { + using CancellationTokenSource timeoutCancellationTokenSource = new CancellationTokenSource(timeout); + using CancellationTokenSource cts = + CancellationTokenSource.CreateLinkedTokenSource(timeoutCancellationTokenSource.Token, ServerShutdownToken); + + return DoReceiveFromAsync(streamSocket, streamSocket.RemoteEndPoint, flags, inputBuffer, cts.Token); + } + + public Task<TransmissionResult> ReceiveFromAsync(EndPoint remoteEndPoint, Memory<byte> inputBuffer, SocketFlags flags, TimeSpan timeout) + { + using CancellationTokenSource timeoutCancellationTokenSource = new CancellationTokenSource(timeout); + using CancellationTokenSource cts = + CancellationTokenSource.CreateLinkedTokenSource(timeoutCancellationTokenSource.Token, ServerShutdownToken); + + return DoReceiveFromAsync(datagramSocket, remoteEndPoint, flags, inputBuffer, cts.Token); + } + + public ValueTask<int> SendAsync(Memory<byte> outputBuffer, SocketFlags flags, TimeSpan timeout) + { + using CancellationTokenSource timeoutCancellationTokenSource = new CancellationTokenSource(timeout); + using CancellationTokenSource cts = + CancellationTokenSource.CreateLinkedTokenSource(timeoutCancellationTokenSource.Token, ServerShutdownToken); + + return DoSendToAsync(streamSocket, streamSocket.RemoteEndPoint, flags, outputBuffer, cts.Token); + } + + public ValueTask<int> SendToAsync(EndPoint remoteEndPoint, Memory<byte> outputBuffer, SocketFlags flags, TimeSpan timeout) + { + using CancellationTokenSource timeoutCancellationTokenSource = new CancellationTokenSource(timeout); + using CancellationTokenSource cts = + CancellationTokenSource.CreateLinkedTokenSource(timeoutCancellationTokenSource.Token, ServerShutdownToken); + + return DoSendToAsync(datagramSocket, remoteEndPoint, flags, outputBuffer, cts.Token); + } + + /// <summary> + /// Configures the logger to log messages to the given stream (or to <see cref="Stream.Null"/> if <c>null</c>) and + /// to only log messages that are of severity <paramref name="minimumLoggedSeverity"/> or higher. + /// </summary> + /// <param name="loggingStream">The stream to which messages will be logged.</param> + /// <param name="minimumLoggedSeverity">The minimum severity a message must be to be logged.</param> + public void SetLoggingStream(Stream? loggingStream, LogLevel minimumLoggedSeverity = LogLevel.Info) + { + lock (loggerLockObject) + { + logger = new Logger(loggingStream ?? Stream.Null, minimumLoggedSeverity); + } + } + + /// <summary> + /// Attempts to asynchronously bind the underlying socket to the given local endpoint. Does not block. + /// If the timeout is exceeded the binding attempt is aborted and the method returns false. + /// </summary> + /// <param name="localEndPoint">The local endpoint to bind to.</param> + /// <param name="timeout">The timeout within which to attempt the binding.</param> + /// <returns>Whether the binding was successful or not.</returns> + public async Task<bool> TryBindAsync(EndPoint localEndPoint, TimeSpan timeout) + { + using CancellationTokenSource timeoutCancellationTokenSource = new CancellationTokenSource(timeout); + using CancellationTokenSource cts = + CancellationTokenSource.CreateLinkedTokenSource(timeoutCancellationTokenSource.Token, ServerShutdownToken); + + try + { + return await Task.Run(() => + { + streamSocket.Bind(localEndPoint); + datagramSocket.Bind(localEndPoint); + + return true; + }, cts.Token); + } + catch (TaskCanceledException) + { + return false; + } + catch (SocketException ex) + { + logger.LogException($"Socket exception on binding socket to {localEndPoint}:", ex); + return false; + } + } + + public async Task<bool> TryConnectAsync(EndPoint remoteEndPoint, TimeSpan timeout) + { + using CancellationTokenSource timeoutCancellationTokenSource = new CancellationTokenSource(timeout); + using CancellationTokenSource cts = + CancellationTokenSource.CreateLinkedTokenSource(timeoutCancellationTokenSource.Token, ServerShutdownToken); + + try + { + await DoConnectAsync(streamSocket, remoteEndPoint, cts.Token); + + return true; + } + catch (TaskCanceledException) + { + return false; + } + catch (SocketException ex) + { + logger.LogException($"Socket exception on connecting socket to {remoteEndPoint}:", ex); + return false; + } + } + + public async Task<bool> TryDisconnectAsync(TimeSpan timeout) + { + using CancellationTokenSource timeoutCancellationTokenSource = new CancellationTokenSource(timeout); + using CancellationTokenSource cts = + CancellationTokenSource.CreateLinkedTokenSource(timeoutCancellationTokenSource.Token, ServerShutdownToken); + + try + { + await DoDisconnectAsync(streamSocket, cts.Token); + + streamSocket.Shutdown(SocketShutdown.Both); + streamSocket.Close(1); + + return true; + } + catch (TaskCanceledException) + { + return false; + } + catch (SocketException ex) + { + logger.LogException($"Socket exception on disconnecting socket:", ex); + return false; + } + } + } +} +\ No newline at end of file diff --git a/NetSharp/NetSharp/Deprecated/ConnectionBuilder.cs b/NetSharp/NetSharp/Deprecated/ConnectionBuilder.cs @@ -0,0 +1,186 @@ +using System; +using System.Collections.Generic; +using System.IO; + +namespace NetSharp.Deprecated +{ + /// <summary> + /// Allows for configuring and subsequently building a <see cref="Connection"/> instance. + /// </summary> + public sealed class ConnectionBuilder + { + private static readonly LoggingSettings DefaultLoggingSettings = + new LoggingSettings(Stream.Null, LogLevel.Warn); + + private static readonly PoolingSettings DefaultPoolingSettings = + new PoolingSettings(10, false); + + private readonly List<Func<Memory<byte>, Memory<byte>>> incomingPipelineStages = + new List<Func<Memory<byte>, Memory<byte>>>(); + + private readonly List<Func<Memory<byte>, Memory<byte>>> outgoingPipelineStages = + new List<Func<Memory<byte>, Memory<byte>>>(); + + private LoggingSettings? loggingSettings; + private PoolingSettings? poolingSettings; + + /// <summary> + /// The number of stages in the currently configured incoming packet pipeline. + /// </summary> + public int IncomingPacketPipelineStageCount + { + get { return incomingPipelineStages.Count; } + } + + /// <summary> + /// The number of stages in the currently configured outgoing packet pipeline. + /// </summary> + public int OutgoingPacketPipelineStageCount + { + get { return outgoingPipelineStages.Count; } + } + + /// <summary> + /// Returns a new <see cref="Connection"/> instance with the current configuration. + /// </summary> + /// <returns>The configured <see cref="Connection"/> instance.</returns> + public Connection Build() + { + PacketPipelineBuilder<Memory<byte>, Memory<byte>, NetworkPacket> incomingPipelineBuilder = + new PacketPipelineBuilder<Memory<byte>, Memory<byte>, NetworkPacket>(); + + incomingPipelineBuilder.WithInputStage(memory => memory); + foreach (Func<Memory<byte>, Memory<byte>> stage in incomingPipelineStages) + { + incomingPipelineBuilder = incomingPipelineBuilder.WithIntermediateStage(stage); + } + + incomingPipelineBuilder.WithOutputStage(NetworkPacket.Deserialise); + + PacketPipelineBuilder<NetworkPacket, Memory<byte>, Memory<byte>> outgoingPipelineBuilder = + new PacketPipelineBuilder<NetworkPacket, Memory<byte>, Memory<byte>>(); + + outgoingPipelineBuilder.WithInputStage(NetworkPacket.Serialise); + foreach (Func<Memory<byte>, Memory<byte>> stage in outgoingPipelineStages) + { + outgoingPipelineBuilder = outgoingPipelineBuilder.WithIntermediateStage(stage); + } + + outgoingPipelineBuilder.WithOutputStage(memory => memory); + + Connection connection = new Connection( + incomingPipelineBuilder.Build(), + outgoingPipelineBuilder.Build(), + poolingSettings?.ObjectPoolSize ?? DefaultPoolingSettings.ObjectPoolSize, + poolingSettings?.PreallocateBuffers ?? DefaultPoolingSettings.PreallocateBuffers, + loggingSettings?.LoggingStream ?? DefaultLoggingSettings.LoggingStream, + loggingSettings?.MinimumLevel ?? DefaultLoggingSettings.MinimumLevel); + + return connection; + } + + /// <summary> + /// Adds an extra pipeline stage to the currently configured incoming packet pipeline, at the given index. + /// </summary> + /// <param name="transform"> + /// The transformation that should be applied when a packet passes through the pipeline. + /// </param> + /// <param name="index">The position in the pipeline at which to place the transform.</param> + /// <returns>The builder instance for further configuration.</returns> + public ConnectionBuilder WithIncomingPipelineStage(in Func<Memory<byte>, Memory<byte>> transform, int index) + { + incomingPipelineStages.Insert(index, transform); + return this; + } + + /// <summary> + /// Sets the logging settings for the currently configured connection. + /// </summary> + /// <param name="settings">The logging settings to use.</param> + /// <returns>The builder instance for further configuration.</returns> + public ConnectionBuilder WithLogging(LoggingSettings settings) + { + loggingSettings = settings; + return this; + } + + /// <summary> + /// Adds an extra pipeline stage to the currently configured outgoing packet pipeline, at the given index. + /// </summary> + /// <param name="transform"> + /// The transformation that should be applied when a packet passes through the pipeline. + /// </param> + /// <param name="index">The position in the pipeline at which to place the transform.</param> + /// <returns>The builder instance for further configuration.</returns> + public ConnectionBuilder WithOutgoingPipelineStage(in Func<Memory<byte>, Memory<byte>> transform, int index) + { + outgoingPipelineStages.Insert(index, transform); + return this; + } + + /// <summary> + /// Sets the pooling settings for the currently configured connection. + /// </summary> + /// <param name="settings">The pooling settings to use.</param> + /// <returns>The builder instance for further configuration.</returns> + public ConnectionBuilder WithPooling(PoolingSettings settings) + { + poolingSettings = settings; + return this; + } + + /// <summary> + /// Holds settings for configuring a connection's logging. + /// </summary> + public readonly struct LoggingSettings + { + /// <summary> + /// The stream to which messages will be logged. + /// </summary> + public readonly Stream LoggingStream; + + /// <summary> + /// The minimum severity that a log message must have to be recorded. + /// </summary> + public readonly LogLevel MinimumLevel; + + /// <summary> + /// Initialises a new instance of the <see cref="LoggingStream"/> struct. + /// </summary> + /// <param name="stream">The stream to which messages will be logged..</param> + /// <param name="minimumLevel">The minimum severity that a log message must have to be recorded.</param> + public LoggingSettings(Stream stream, LogLevel minimumLevel) + { + LoggingStream = stream; + MinimumLevel = minimumLevel; + } + } + + /// <summary> + /// Holds settings for configuring a connection's buffer pooling. + /// </summary> + public readonly struct PoolingSettings + { + /// <summary> + /// The number of objects that will be held in the object pools. + /// </summary> + public readonly int ObjectPoolSize; + + /// <summary> + /// Whether the buffers for receiving messages should be preallocated. + /// </summary> + public readonly bool PreallocateBuffers; + + /// <summary> + /// Initialises a new instance of the <see cref="PoolingSettings"/> struct. + /// </summary> + /// <param name="poolSize">The number of objects that will be held in the object pools.</param> + /// <param name="preallocateBuffers">Whether the buffers for receiving messages should be preallocated.</param> + public PoolingSettings(int poolSize, bool preallocateBuffers) + { + ObjectPoolSize = poolSize; + PreallocateBuffers = preallocateBuffers; + } + } + } +} +\ No newline at end of file diff --git a/NetSharp/NetSharp/Deprecated/ConnectionBuilderExtensions.cs b/NetSharp/NetSharp/Deprecated/ConnectionBuilderExtensions.cs @@ -0,0 +1,27 @@ +using System; +using System.IO; + +namespace NetSharp.Deprecated +{ + /// <summary> + /// Provides additional methods and functionality to the <see cref="ConnectionBuilder"/> class. + /// </summary> + public static class ConnectionBuilderExtensions + { + public static ConnectionBuilder AppendIncomingPipelineStage(this ConnectionBuilder instance, + in Func<Memory<byte>, Memory<byte>> transform) + => instance.WithIncomingPipelineStage(transform, instance.IncomingPacketPipelineStageCount); + + public static ConnectionBuilder AppendOutgoingPipelineStage(this ConnectionBuilder instance, + in Func<Memory<byte>, Memory<byte>> transform) + => instance.WithOutgoingPipelineStage(transform, instance.OutgoingPacketPipelineStageCount); + + public static ConnectionBuilder WithLogging(this ConnectionBuilder instance, + Stream loggingStream, LogLevel minimumLogLevel) + => instance.WithLogging(new ConnectionBuilder.LoggingSettings(loggingStream, minimumLogLevel)); + + public static ConnectionBuilder WithPooling(this ConnectionBuilder instance, + int poolSize, bool preallocateBuffers) + => instance.WithPooling(new ConnectionBuilder.PoolingSettings(poolSize, preallocateBuffers)); + } +} +\ No newline at end of file diff --git a/NetSharp/NetSharp/Deprecated/ConnectionExtensions.cs b/NetSharp/NetSharp/Deprecated/ConnectionExtensions.cs @@ -0,0 +1,73 @@ +using NetSharp.Utils; + +using System; +using System.Net; +using System.Net.Sockets; +using System.Threading; +using System.Threading.Tasks; + +namespace NetSharp.Deprecated +{ + /// <summary> + /// Provides additional methods and functionality to the <see cref="Connection"/> class. + /// </summary> + public static class ConnectionExtensions + { + public static Task<TransmissionResult> ReceiveAsync(this Connection instance, + EndPoint remoteEndPoint, Memory<byte> inputBuffer, SocketFlags flags) + => instance.ReceiveAsync(inputBuffer, flags, Timeout.InfiniteTimeSpan); + + public static Task<TransmissionResult> ReceiveFromAsync(this Connection instance, + EndPoint remoteEndPoint, Memory<byte> inputBuffer, SocketFlags flags) + => instance.ReceiveFromAsync(remoteEndPoint, inputBuffer, flags, Timeout.InfiniteTimeSpan); + + public static ValueTask<int> SendAsync(this Connection instance, + EndPoint remoteEndPoint, Memory<byte> outputBuffer, SocketFlags flags) + => instance.SendAsync(outputBuffer, flags, Timeout.InfiniteTimeSpan); + + public static ValueTask<int> SendToAsync(this Connection instance, + EndPoint remoteEndPoint, Memory<byte> outputBuffer, SocketFlags flags) + => instance.SendToAsync(remoteEndPoint, outputBuffer, flags, Timeout.InfiniteTimeSpan); + + /// <summary> + /// Attempts to synchronously bind the underlying socket to the given local endpoint. Blocks. + /// If the timeout is exceeded the binding attempt is aborted and the method returns false. + /// </summary> + /// <param name="localEndPoint">The local endpoint to bind to.</param> + /// <param name="timeout">The timeout within which to attempt the binding.</param> + /// <returns>Whether the binding was successful or not.</returns> + public static bool TryBind(this Connection instance, + EndPoint localEndPoint, TimeSpan timeout) + => instance.TryBindAsync(localEndPoint, timeout).Result; + + public static bool TryBind(this Connection instance, + EndPoint localEndPoint) + => instance.TryBindAsync(localEndPoint, Timeout.InfiniteTimeSpan).Result; + + public static Task<bool> TryBindAsync(this Connection instance, + EndPoint localEndPoint) + => instance.TryBindAsync(localEndPoint, Timeout.InfiniteTimeSpan); + + public static bool TryConnect(this Connection instance, + EndPoint remoteEndPoint) + => instance.TryConnectAsync(remoteEndPoint, Timeout.InfiniteTimeSpan).Result; + + public static bool TryConnect(this Connection instance, + EndPoint remoteEndPoint, TimeSpan timeout) + => instance.TryConnectAsync(remoteEndPoint, timeout).Result; + + public static Task<bool> TryConnectAsync(this Connection instance, + EndPoint remoteEndPoint) + => instance.TryConnectAsync(remoteEndPoint, Timeout.InfiniteTimeSpan); + + public static bool TryDisconnect(this Connection instance) + => instance.TryDisconnectAsync(Timeout.InfiniteTimeSpan).Result; + + public static bool TryDisconnect(this Connection instance, + TimeSpan timeout) + => instance.TryDisconnectAsync(timeout).Result; + + public static Task<bool> TryDisconnectAsync(this Connection instance) + => instance.TryDisconnectAsync(Timeout.InfiniteTimeSpan); + } +} +\ No newline at end of file diff --git a/NetSharp/NetSharp/Deprecated/Constants.cs b/NetSharp/NetSharp/Deprecated/Constants.cs @@ -0,0 +1,15 @@ +namespace NetSharp.Deprecated +{ + /// <summary> + /// Holds internal default configurations and constants. + /// </summary> + internal static class Constants + { + /// <summary> + /// The default port over which a connection is made. + /// </summary> + internal const int DefaultPort = 12374; + + internal const int MaximumUdpPacketBytes = 65507; + } +} +\ No newline at end of file diff --git a/NetSharp/NetSharp/Deprecated/CryptographyHelpers.cs b/NetSharp/NetSharp/Deprecated/CryptographyHelpers.cs @@ -0,0 +1,96 @@ +using System; +using System.IO; +using System.Security.Cryptography; +using System.Text; + +namespace NetSharp.Deprecated +{ + internal static class CryptographyHelpers + { + #region Settings + + private static string _hash = "SHA1"; + private static int _iterations = 2; + private static int _keySize = 256; + private static string _salt = "aselrias38490a32"; // Random + private static string _vector = "8947az34awl34kjq"; // Random + + #endregion Settings + + public static string Decrypt(byte[] value, string password) + { + return Decrypt<AesManaged>(value, password); + } + + public static string Decrypt<T>(byte[] value, string password) where T : SymmetricAlgorithm, new() + { + byte[] vectorBytes = Encoding.ASCII.GetBytes(_vector); // GetBytes<ASCIIEncoding>(_vector); + byte[] saltBytes = Encoding.ASCII.GetBytes(_salt); // GetBytes<ASCIIEncoding>(_salt); + byte[] valueBytes = value; + + byte[] decrypted; + int decryptedByteCount = 0; + + using (T cipher = new T()) + { + PasswordDeriveBytes _passwordBytes = new PasswordDeriveBytes(password, saltBytes, _hash, _iterations); + byte[] keyBytes = _passwordBytes.GetBytes(_keySize / 8); + + cipher.Mode = CipherMode.CBC; + + try + { + using (ICryptoTransform decryptor = cipher.CreateDecryptor(keyBytes, vectorBytes)) + { + using MemoryStream from = new MemoryStream(valueBytes); + using CryptoStream reader = new CryptoStream(@from, decryptor, CryptoStreamMode.Read); + + decrypted = new byte[valueBytes.Length]; + decryptedByteCount = reader.Read(decrypted, 0, decrypted.Length); + } + } + catch (Exception ex) + { + return String.Empty; + } + + cipher.Clear(); + } + return Encoding.UTF8.GetString(decrypted, 0, decryptedByteCount); + } + + public static byte[] Encrypt(string value, string password) + { + return Encrypt<AesManaged>(value, password); + } + + public static byte[] Encrypt<T>(string value, string password) where T : SymmetricAlgorithm, new() + { + byte[] vectorBytes = Encoding.ASCII.GetBytes(_vector); // GetBytes<ASCIIEncoding>(_vector); + byte[] saltBytes = Encoding.ASCII.GetBytes(_salt); // GetBytes<ASCIIEncoding>(_salt); + byte[] valueBytes = Encoding.UTF8.GetBytes(value); // GetBytes<UTF8Encoding>(value); + + byte[] encrypted; + using (T cipher = new T()) + { + PasswordDeriveBytes _passwordBytes = + new PasswordDeriveBytes(password, saltBytes, _hash, _iterations); + byte[] keyBytes = _passwordBytes.GetBytes(_keySize / 8); + + cipher.Mode = CipherMode.CBC; + + using (ICryptoTransform encryptor = cipher.CreateEncryptor(keyBytes, vectorBytes)) + { + using MemoryStream to = new MemoryStream(); + using CryptoStream writer = new CryptoStream(to, encryptor, CryptoStreamMode.Write); + + writer.Write(valueBytes, 0, valueBytes.Length); + writer.FlushFinalBlock(); + encrypted = to.ToArray(); + } + cipher.Clear(); + } + return encrypted; + } + } +} +\ No newline at end of file diff --git a/NetSharp/NetSharp/Deprecated/Logger.cs b/NetSharp/NetSharp/Deprecated/Logger.cs @@ -0,0 +1,194 @@ +using System; +using System.IO; +using System.Text; +using System.Threading.Tasks; + +namespace NetSharp.Deprecated +{ + /// <summary> + /// Specifies the severity level of a log message. + /// </summary> + public enum LogLevel + { + /// <summary> + /// The logged message contains some information. Lowest severity. + /// </summary> + Info, + + /// <summary> + /// The logged message contains a warning. Higher severity. + /// </summary> + Warn, + + /// <summary> + /// The logged message contains details about an error. Higher severity. + /// </summary> + Error, + + /// <summary> + /// The logged message contains details about an exception. Highest severity. + /// </summary> + Exception + } + + /// <summary> + /// A simple logger capable of writing text to a stream. + /// </summary> + public readonly struct Logger : IDisposable + { + /// <summary> + /// The stream to which messages will be logged. + /// </summary> + private readonly Stream loggingStream; + + /// <summary> + /// The minimum severity that log messages need to be logged to the underlying stream. + /// </summary> + private readonly LogLevel minimumSeverity; + + /// <summary> + /// The text writer we will use to log messages to the underlying stream. + /// </summary> + private readonly StreamWriter writer; + + /// <summary> + /// Initialises a new instance of the <see cref="Logger"/> struct. + /// </summary> + /// <param name="outputStream">The stream that the logger instance should log messages to.</param> + /// <param name="minimumLogSeverity">The minimum log level that will be logged to the output stream.</param> + public Logger(Stream outputStream, LogLevel minimumLogSeverity = LogLevel.Info) + { + loggingStream = outputStream; + writer = new StreamWriter(loggingStream, Encoding.Default) { AutoFlush = true }; + + minimumSeverity = minimumLogSeverity; + } + + /// <inheritdoc /> + public void Dispose() + { + loggingStream.Dispose(); + writer.Dispose(); + } + + /// <summary> + /// Logs a message to the underlying stream, along with the given exception and at the given severity. + /// </summary> + /// <param name="message">The message that should be logged.</param> + /// <param name="exception">The exception that occurred (if any).</param> + /// <param name="severity">The severity of the message that is being logged.</param> + public void Log(string message, Exception? exception, LogLevel severity) + { + if (severity < minimumSeverity) return; + + string severityTag = severity switch + { + LogLevel.Info => "Info ", + LogLevel.Warn => "Warn ", + LogLevel.Error => "Error", + LogLevel.Exception => "Excep", + _ => "Info " + }; + + writer.WriteLine($"[{severityTag}] {message} {exception}"); + } + + /// <summary> + /// Logs a message asynchronously to the underlying stream, along with the given exception and at the given severity. + /// </summary> + /// <param name="message">The message that should be logged.</param> + /// <param name="exception">The exception that occurred (if any).</param> + /// <param name="severity">The severity of the message that is being logged.</param> + public async Task LogAsync(string message, Exception? exception, LogLevel severity) + { + if (loggingStream.Equals(Stream.Null)) + { + // ignore log request if the underlying stream is null + return; + } + + if (!exception?.Equals(default) ?? false) + { + severity = LogLevel.Exception; + } + + if (severity >= minimumSeverity) + { + string severityTag = severity switch + { + LogLevel.Info => "Info ", + LogLevel.Warn => "Warn ", + LogLevel.Error => "Error", + LogLevel.Exception => "Excep", + _ => "Info " + }; + + await writer.WriteLineAsync($"[{severityTag}] {message} {exception}"); + } + } + + /// <summary> + /// Logs an error to the underlying stream, with severity <see cref="LogLevel.Info"/>. + /// </summary> + /// <param name="message">The error that should be logged.</param> + public void LogError(string message) => Log(message, null, LogLevel.Error); + + /// <summary> + /// Logs an error to the underlying stream asynchronously, with severity <see cref="LogLevel.Error"/>. + /// </summary> + /// <param name="message">The error that should be logged.</param> + public async Task LogErrorAsync(string message) => await LogAsync(message, null, LogLevel.Error); + + /// <summary> + /// Logs an exception to the underlying stream, with severity <see cref="LogLevel.Exception"/>. + /// </summary> + /// <param name="exception">The exception that should be logged.</param> + public void LogException(Exception exception) => Log("", exception, LogLevel.Exception); + + /// <summary> + /// Logs an exception to the underlying stream, along with a short debug message, with severity + /// <see cref="LogLevel.Exception"/>. + /// </summary> + /// <param name="message">The debug message that should be logged with the exception.</param> + /// <param name="exception">The exception that should be logged.</param> + public void LogException(string message, Exception exception) => Log(message, exception, LogLevel.Exception); + + /// <summary> + /// Logs an exception to the underlying stream asynchronously, with severity <see cref="LogLevel.Exception"/>. + /// </summary> + /// <param name="exception">The exception that should be logged.</param> + public async Task LogExceptionAsync(Exception exception) => await LogAsync("", exception, LogLevel.Exception); + + /// <summary> + /// Logs an exception to the underlying stream asynchronously, along with a short debug message, with severity + /// <see cref="LogLevel.Exception"/>. + /// </summary> + /// <param name="message">The debug message that should be logged with the exception.</param> + /// <param name="exception">The exception that should be logged.</param> + public async Task LogExceptionAsync(string message, Exception exception) => await LogAsync(message, exception, LogLevel.Exception); + + /// <summary> + /// Logs a message to the underlying stream, with severity <see cref="LogLevel.Info"/>. + /// </summary> + /// <param name="message">The message that should be logged.</param> + public void LogMessage(string message) => Log(message, null, LogLevel.Info); + + /// <summary> + /// Logs a message to the underlying stream asynchronously, with severity <see cref="LogLevel.Info"/>. + /// </summary> + /// <param name="message">The message that should be logged.</param> + public async Task LogMessageAsync(string message) => await LogAsync(message, null, LogLevel.Info); + + /// <summary> + /// Logs a warning to the underlying stream, with severity <see cref="LogLevel.Info"/>. + /// </summary> + /// <param name="message">The warning that should be logged.</param> + public void LogWarning(string message) => Log(message, null, LogLevel.Warn); + + /// <summary> + /// Logs a warning to the underlying stream asynchronously, with severity <see cref="LogLevel.Warn"/>. + /// </summary> + /// <param name="message">The warning that should be logged.</param> + public async Task LogWarningAsync(string message) => await LogAsync(message, null, LogLevel.Warn); + } +} +\ No newline at end of file diff --git a/NetSharp/NetSharp/Deprecated/NetworkErrorCode.cs b/NetSharp/NetSharp/Deprecated/NetworkErrorCode.cs @@ -0,0 +1,18 @@ +namespace NetSharp.Deprecated +{ + /// <summary> + /// Enumerates the possible error codes for network operations, being held in the packet. + /// </summary> + public enum NetworkErrorCode : uint + { + /// <summary> + /// Signifies that there was no error during transmission. + /// </summary> + Ok = 0, + + /// <summary> + /// A generic error occurred during packet transmission. + /// </summary> + Error = 1 << 1, + } +} +\ No newline at end of file diff --git a/NetSharp/NetSharp/Deprecated/NetworkPacket.cs b/NetSharp/NetSharp/Deprecated/NetworkPacket.cs @@ -0,0 +1,218 @@ +using NetSharp.Deprecated.Conversion; + +using System; + +namespace NetSharp.Deprecated +{ + /// <summary> + /// Represents a low-level packet that is transmitted over the network. + /// </summary> + public readonly struct NetworkPacket + { + /// <summary> + /// Initialises a new instance of the <see cref="NetworkPacket"/> struct. + /// </summary> + /// <param name="data">The data that should be transmitted in the packet.</param> + /// <param name="header">The header for the packet.</param> + /// <param name="footer">The footer for the packet.</param> + private NetworkPacket(ReadOnlyMemory<byte> data, NetworkPacketHeader header, NetworkPacketFooter footer) + { + Header = header; + + DataBuffer = data; + + Footer = footer; + } + + /// <summary> + /// The number of bytes allocated in each packet for user data. + /// </summary> + public const int DataSegmentSize = PacketSize - HeaderSize - FooterSize; + + /// <summary> + /// The number of bytes taken up in each packet by its footer. + /// </summary> + public const int FooterSize = NetworkPacketFooter.Size; + + /// <summary> + /// The number of bytes taken up in each packet by its header. + /// </summary> + public const int HeaderSize = NetworkPacketHeader.Size; + + /// <summary> + /// The size of each packet, including its header, footer, and data segment. + /// </summary> + public const int PacketSize = 4096; + + /// <summary> + /// The data held in this packet. + /// </summary> + public readonly ReadOnlyMemory<byte> DataBuffer; + + public readonly NetworkPacketFooter Footer; + public readonly NetworkPacketHeader Header; + + /// <summary> + /// Initialises a new instance of the <see cref="NetworkPacket"/> struct. + /// </summary> + /// <param name="data">The data that should be transmitted in the packet.</param> + /// <param name="dataLength">The number of bytes that are held in the given data buffer.</param> + /// <param name="type">The packet type.</param> + /// <param name="errorCode">The error code associated with this transmission.</param> + /// <param name="hasSucceedingPacket">Whether this packet has a succeeding packet in the packet chain.</param> + public NetworkPacket(ReadOnlyMemory<byte> data, int dataLength, uint type, NetworkErrorCode errorCode, bool hasSucceedingPacket) + { + Header = new NetworkPacketHeader(type, errorCode, dataLength); + + DataBuffer = data; + + Footer = new NetworkPacketFooter(hasSucceedingPacket); + } + + /// <summary> + /// Deserialises the given buffer into a packet instance. + /// </summary> + /// <param name="buffer">The byte buffer to serialise.</param> + /// <returns>The deserialised packet instance.</returns> + public static NetworkPacket Deserialise(Memory<byte> buffer) + { + Span<byte> serialisedPacketHeader = buffer.Slice(0, HeaderSize).Span; + NetworkPacketHeader header = NetworkPacketHeader.Deserialise(serialisedPacketHeader); + + Span<byte> serialisedPacketFooter = buffer.Slice(HeaderSize + DataSegmentSize, FooterSize).Span; + NetworkPacketFooter footer = NetworkPacketFooter.Deserialise(serialisedPacketFooter); + + Memory<byte> serialisedInstanceData = buffer.Slice(HeaderSize, DataSegmentSize); + + return new NetworkPacket(serialisedInstanceData, header, footer); + } + + /// <summary> + /// Serialises the given packet instance to a new byte buffer. + /// </summary> + /// <param name="instance">The packet instance to serialise.</param> + /// <returns>The byte buffer that represents the packet instance.</returns> + public static Memory<byte> Serialise(NetworkPacket instance) + { + byte[] buffer = new byte[PacketSize]; + SerialiseToBuffer(buffer, instance); + return buffer; + } + + /// <summary> + /// Serialises the given packet instance into the given byte buffer. + /// </summary> + /// <param name="buffer"> + /// The buffer to which the instance should be serialised. Must be at least of size <see cref="PacketSize"/>. + /// </param> + /// <param name="instance">The packet instance to serialise.</param> + /// <exception cref="ArgumentException">Thrown if the given buffer is too small.</exception> + public static void SerialiseToBuffer(Memory<byte> buffer, NetworkPacket instance) + { + if (buffer.Length < PacketSize) + { + throw new ArgumentException("Given buffer is too small to serialise the packet instance into.", nameof(buffer)); + } + + Span<byte> serialisedPacketHeader = buffer.Slice(0, HeaderSize).Span; + NetworkPacketHeader.Serialise(serialisedPacketHeader, instance.Header); + + Span<byte> serialisedPacketFooter = buffer.Slice(HeaderSize + DataSegmentSize, FooterSize).Span; + NetworkPacketFooter.Serialise(serialisedPacketFooter, instance.Footer); + + Memory<byte> serialisedInstanceData = buffer.Slice(HeaderSize, DataSegmentSize); + instance.DataBuffer.CopyTo(serialisedInstanceData); + } + } + + // TODO: Document + public readonly struct NetworkPacketFooter + { + private const int PacketHasNextStart = 0; + + /// <summary> + /// The number of bytes taken up by a packet footer. + /// </summary> + public const int Size = sizeof(bool); + + public readonly bool HasSucceedingPacket; + + public NetworkPacketFooter(bool hasSucceedingPacket) + { + HasSucceedingPacket = hasSucceedingPacket; + } + + public static NetworkPacketFooter Deserialise(Span<byte> buffer) + { + Span<byte> serialisedHasNextFlag = buffer.Slice(PacketHasNextStart, sizeof(bool)); + + return new NetworkPacketFooter( + EndianAwareBitConverter.ToBoolean(serialisedHasNextFlag)); + } + + public static void Serialise(Span<byte> buffer, NetworkPacketFooter instance) + { + Span<byte> serialisedHasNextFlag = buffer.Slice(PacketHasNextStart, sizeof(bool)); + + EndianAwareBitConverter.GetBytes(instance.HasSucceedingPacket).CopyTo(serialisedHasNextFlag); + } + } + + // TODO: Document + public readonly struct NetworkPacketHeader + { + private const int PacketDataLengthStart = 2 * sizeof(uint); + private const int PacketErrorCodeStart = sizeof(uint); + private const int PacketTypeStart = 0; + + /// <summary> + /// The number of bytes taken up by a packet header. + /// </summary> + public const int Size = sizeof(uint) + sizeof(uint) + sizeof(int); + + /// <summary> + /// The number of bytes of data held in the packet. + /// </summary> + public readonly int DataLength; + + /// <summary> + /// The error code for this packet. + /// </summary> + public readonly NetworkErrorCode ErrorCode; + + /// <summary> + /// The packet type. + /// </summary> + public readonly uint Type; + + public NetworkPacketHeader(uint packetType, NetworkErrorCode packetErrorCode, int packetDataLength) + { + Type = packetType; + ErrorCode = packetErrorCode; + DataLength = packetDataLength; + } + + public static NetworkPacketHeader Deserialise(Span<byte> buffer) + { + Span<byte> serialisedType = buffer.Slice(PacketTypeStart, sizeof(uint)); + Span<byte> serialisedErrorCode = buffer.Slice(PacketErrorCodeStart, sizeof(uint)); + Span<byte> serialisedDataLength = buffer.Slice(PacketDataLengthStart, sizeof(int)); + + return new NetworkPacketHeader( + EndianAwareBitConverter.ToUInt32(serialisedType), + (NetworkErrorCode)EndianAwareBitConverter.ToUInt32(serialisedErrorCode), + EndianAwareBitConverter.ToInt32(serialisedDataLength)); + } + + public static void Serialise(Span<byte> buffer, NetworkPacketHeader instance) + { + Span<byte> serialisedType = buffer.Slice(PacketTypeStart, sizeof(uint)); + Span<byte> serialisedErrorCode = buffer.Slice(PacketErrorCodeStart, sizeof(uint)); + Span<byte> serialisedDataLength = buffer.Slice(PacketDataLengthStart, sizeof(int)); + + EndianAwareBitConverter.GetBytes(instance.Type).CopyTo(serialisedType); + EndianAwareBitConverter.GetBytes((uint)instance.ErrorCode).CopyTo(serialisedErrorCode); + EndianAwareBitConverter.GetBytes(instance.DataLength).CopyTo(serialisedDataLength); + } + } +} +\ No newline at end of file diff --git a/NetSharp/NetSharp/Deprecated/PacketPipeline.cs b/NetSharp/NetSharp/Deprecated/PacketPipeline.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace NetSharp.Deprecated +{ + /// <summary> + /// Represents a pipeline of transformations that packets must undergo. + /// </summary> + /// <typeparam name="TInput">The type of packet the pipeline receives.</typeparam> + /// <typeparam name="TIntermediate">The type of packet the pipeline internally handles.</typeparam> + /// <typeparam name="TOutput">The type of packet the pipeline outputs.</typeparam> + // TODO: Implement a packet pipeline, with multiple transform stages to allow encryption, compression, and various other bytewise manipulation stages. + internal readonly struct PacketPipeline<TInput, TIntermediate, TOutput> + { + private readonly PacketPipelineStage<TInput, TIntermediate> pipelineInputStage; + private readonly IReadOnlyCollection<PacketPipelineStage<TIntermediate, TIntermediate>> pipelineIntermediateStages; + private readonly PacketPipelineStage<TIntermediate, TOutput> pipelineOutputStage; + + internal PacketPipeline( + PacketPipelineStage<TInput, TIntermediate> firstStage, + PacketPipelineStage<TIntermediate, TOutput> lastStage, + IReadOnlyCollection<PacketPipelineStage<TIntermediate, TIntermediate>> intermediateStages) + { + pipelineInputStage = firstStage; + pipelineOutputStage = lastStage; + + pipelineIntermediateStages = intermediateStages; + } + + /// <summary> + /// Passes the given packet through the pipeline. + /// </summary> + /// <param name="inputPacket">The incoming packet.</param> + /// <returns>The outgoing transformed packet.</returns> + internal TOutput ProcessPacket(TInput inputPacket) + { + TIntermediate intermediatePacket = pipelineInputStage.Process(inputPacket); + + intermediatePacket = pipelineIntermediateStages.Aggregate(intermediatePacket, (current, stage) => stage.Process(current)); + + return pipelineOutputStage.Process(intermediatePacket); + } + } + + /// <summary> + /// Represents a single transformation applied to a packet traveling through the pipeline. + /// </summary> + /// <typeparam name="TInput">The type the transformation takes as input.</typeparam> + /// <typeparam name="TOutput">The type the transformation produces as output.</typeparam> + internal readonly struct PacketPipelineStage<TInput, TOutput> + { + private readonly Func<TInput, TOutput> stageDelegate; + + internal PacketPipelineStage(in Func<TInput, TOutput> stageProcessingDelegate) + { + stageDelegate = stageProcessingDelegate; + } + + internal TOutput Process(TInput input) + { + return stageDelegate(input); + } + } +} +\ No newline at end of file diff --git a/NetSharp/NetSharp/Deprecated/PacketPipelineBuilder.cs b/NetSharp/NetSharp/Deprecated/PacketPipelineBuilder.cs @@ -0,0 +1,87 @@ +using System; +using System.Collections.Generic; + +namespace NetSharp.Deprecated +{ + /// <summary> + /// Allows for configuring and subsequently building a <see cref="PacketPipeline{TInput,TIntermediate,TOutput}"/> instance. + /// </summary> + /// <typeparam name="TInput">The type of packet that will be submitted to the pipeline.</typeparam> + /// <typeparam name="TIntermediate">The type of packet that will be handled internally by the pipeline.</typeparam> + /// <typeparam name="TOutput">The type of packet that will be requested from the pipeline.</typeparam> + internal sealed class PacketPipelineBuilder<TInput, TIntermediate, TOutput> + { + private readonly List<PacketPipelineStage<TIntermediate, TIntermediate>> intermediateStages; + + private PacketPipelineStage<TInput, TIntermediate>? inputStage; + private PacketPipelineStage<TIntermediate, TOutput>? outputStage; + + internal PacketPipelineBuilder() + { + intermediateStages = new List<PacketPipelineStage<TIntermediate, TIntermediate>>(); + } + + /// <summary> + /// Returns the currently configured <see cref="PacketPipeline{TInput,TIntermediate,TOutput}"/> instance. + /// </summary> + /// <returns>The configured <see cref="PacketPipeline{TInput,TIntermediate,TOutput}"/> instance.</returns> + /// <exception cref="ArgumentNullException"> + /// Thrown when either <see cref="WithInputStage"/> or <see cref="WithOutputStage"/> have not been called. + /// </exception> + internal PacketPipeline<TInput, TIntermediate, TOutput> Build() + { + if (inputStage == null) + { + throw new ArgumentNullException(nameof(inputStage), $"{nameof(WithInputStage)} has not been called."); + } + + if (outputStage == null) + { + throw new ArgumentNullException(nameof(outputStage), $"{nameof(WithOutputStage)} has not been called."); + } + + return new PacketPipeline<TInput, TIntermediate, TOutput>(inputStage.Value, outputStage.Value, intermediateStages); + } + + /// <summary> + /// Configures the input stage for the pipeline. + /// </summary> + /// <param name="stage"> + /// The transformation that should be applied to incoming packets, to convert them from the <typeparamref name="TInput"/> + /// type to the <typeparamref name="TIntermediate"/> type that the pipeline handles internally. + /// </param> + /// <returns>The builder instance for further configuration.</returns> + internal PacketPipelineBuilder<TInput, TIntermediate, TOutput> WithInputStage(in Func<TInput, TIntermediate> stage) + { + inputStage = new PacketPipelineStage<TInput, TIntermediate>(in stage); + return this; + } + + /// <summary> + /// Adds the given intermediate stage to the pipeline. + /// </summary> + /// <param name="stage"> + /// The transformation that should be applied to packets traveling through the pipeline. + /// </param> + /// <returns>The builder instance for further configuration.</returns> + internal PacketPipelineBuilder<TInput, TIntermediate, TOutput> WithIntermediateStage(in Func<TIntermediate, TIntermediate> stage) + { + intermediateStages.Add(new PacketPipelineStage<TIntermediate, TIntermediate>(in stage)); + return this; + } + + /// <summary> + /// Configures the output stage for the pipeline. + /// </summary> + /// <param name="stage"> + /// The transformation that should be applied to outgoing packets, to convert them from the + /// <typeparamref name="TIntermediate"/> type used internally to the <typeparamref name="TOutput"/> type. + /// </param> + /// <returns>The builder instance for further configuration.</returns> + internal PacketPipelineBuilder<TInput, TIntermediate, TOutput> WithOutputStage(in Func<TIntermediate, TOutput> stage) + { + outputStage = new PacketPipelineStage<TIntermediate, TOutput>(in stage); + return this; + } + } +} +\ No newline at end of file diff --git a/NetSharp/NetSharp/Deprecated/PacketRegistry.cs b/NetSharp/NetSharp/Deprecated/PacketRegistry.cs @@ -0,0 +1,285 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Runtime.CompilerServices; + +namespace NetSharp.Deprecated +{ + /// <summary> + /// Provides method of registering request packets and their relevant response packets, as well as mapping their ids. + /// </summary> + internal static class PacketRegistry + { + /// <summary> + /// The start id for automatically generated packet type ids. Any custom packet type ids lower than this value + /// that come from external assemblies will be incremented by this value, to ensure that there are no clashes. + /// </summary> + private const uint AutomaticPacketTypeIdStartPoint = 100; + + /// <summary> + /// The lock object for synchronising access to the <see cref="currentAutomaticPacketTypeIdCounter"/> field. + /// </summary> + private static readonly object currentAutomaticPacketTypeIdCounterLockObject = new object(); + + /// <summary> + /// Maps a packet type id to its relevant packet type, and vice-versa. + /// </summary> + private static readonly BiDictionary<uint, Type> idToPacketTypeMap; + + /// <summary> + /// The assembly that represents the library, where all of the builtin packets are defined. + /// </summary> + private static readonly Assembly LibraryAssembly = Assembly.GetAssembly(typeof(PacketRegistry)); + + /// <summary> + /// Maps a request packet to its relevant response packet, and vice-versa. + /// </summary> + private static readonly BiDictionary<Type, Type> requestToResponseMap; + + /// <summary> + /// The current id for registered packets. + /// </summary> + private static uint currentAutomaticPacketTypeIdCounter = AutomaticPacketTypeIdStartPoint; + + /// <summary> + /// Fetches the packet type id of the given packet type. If the packet type is declared outside of the library + /// assembly, then its value is incremented by the <see cref="AutomaticPacketTypeIdStartPoint"/> value. This ensure that + /// there are no clashes between the packet type ids of packets declared in the library and external packets. + /// </summary> + /// <param name="packetType">The packet type whose id should be fetched.</param> + /// <returns>The id of the given packet type.</returns> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint GetNewPacketTypeId(Type packetType) + { + uint packetTypeId; + + if (packetType.Assembly != LibraryAssembly) + { + lock (currentAutomaticPacketTypeIdCounterLockObject) + { + packetTypeId = currentAutomaticPacketTypeIdCounter++; + } + } + else + { + PacketTypeIdAttribute customPacketTypeIdAttribute = + (PacketTypeIdAttribute)packetType.GetCustomAttributes(typeof(PacketTypeIdAttribute)).First(); + + packetTypeId = customPacketTypeIdAttribute.Id; + } + + return packetTypeId; + } + + /// <summary> + /// Deregisters the given packet type from the registry. + /// </summary> + /// <param name="requestPacketType">The request packet type to deregister, if it is registered.</param> + /// <param name="responsePacketType">The response packet associated with the request packet.</param> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static void DeregisterPacketType(Type requestPacketType, Type? responsePacketType) + { + if (idToPacketTypeMap.ContainsValue(requestPacketType)) + { + idToPacketTypeMap.TryClearKey(requestPacketType, out _); + } + + // if the given response packet is null, then skip deregistering a response packet type + if (responsePacketType == default) return; + + if (!idToPacketTypeMap.ContainsValue(responsePacketType)) + { + idToPacketTypeMap.TryClearKey(responsePacketType, out _); + } + + if (!requestToResponseMap.ContainsValue(requestPacketType)) + { + requestToResponseMap.TryClearKey(requestPacketType, out _); + } + } + + /// <summary> + /// Deregisters the given packet types from the registry. + /// </summary> + /// <param name="requestToResponsePacketTypeMap">The list of packet types to deregister, if they are registered.</param> + internal static void DeregisterPacketTypes(Dictionary<Type, Type?> requestToResponsePacketTypeMap) + { + foreach ((Type requestPacketType, Type? responsePacketType) in requestToResponsePacketTypeMap) + { + DeregisterPacketType(requestPacketType, responsePacketType); + } + } + + /// <summary> + /// Returns the packet type id associated with the given packet type. + /// </summary> + /// <param name="packetType">The packet type whose id to fetch.</param> + /// <returns>The id of the packet type given.</returns> + internal static uint GetPacketId(Type packetType) => idToPacketTypeMap[packetType]; + + /// <summary> + /// Returns the packet type id associated with the given packet type. + /// </summary> + /// <typeparam name="TPacket">The packet type whose id to fetch.</typeparam> + /// <returns>The id of the packet type given.</returns> + internal static uint GetPacketId<TPacket>() where TPacket : IPacket => idToPacketTypeMap[typeof(TPacket)]; + + /// <summary> + /// Returns the packet type associated with the given id. + /// </summary> + /// <param name="packetTypeId">The packet id whose mapped type to fetch.</param> + /// <returns>The packet type mapped by the given id.</returns> + internal static Type GetPacketType(uint packetTypeId) => idToPacketTypeMap[packetTypeId]; + + /// <summary> + /// Returns the type of request packet mapped by the given response packet type. + /// </summary> + /// <typeparam name="TResponse">The response packet type whose request packet type to fetch.</typeparam> + /// <returns>The request packet type, <c>null</c> if no type is mapped.</returns> + internal static Type GetRequestPacketType<TResponse>() where TResponse : IResponsePacket<IRequestPacket> + { + requestToResponseMap.TryGetKey(typeof(TResponse), out Type requestPacketType); + + return requestPacketType; + } + + /// <summary> + /// Returns the type of request packet mapped by the given response packet type. + /// </summary> + /// <param name="responsePacketType">The response packet type whose request packet type to fetch.</param> + /// <returns>The request packet type, <c>null</c> if no type is mapped.</returns> + internal static Type GetRequestPacketType(Type responsePacketType) + { + requestToResponseMap.TryGetKey(responsePacketType, out Type requestPacketType); + + return requestPacketType; + } + + /// <summary> + /// Returns the type of response packet mapped by the given request packet type. + /// </summary> + /// <typeparam name="TRequest">The request packet type whose response packet type to fetch.</typeparam> + /// <returns>The response packet type, <c>null</c> if no type is mapped.</returns> + internal static Type? GetResponsePacketType<TRequest>() where TRequest : IRequestPacket + { + return requestToResponseMap.TryGetValue(typeof(TRequest), out Type responsePacketType) ? responsePacketType : default; + } + + /// <summary> + /// Returns the type of response packet mapped by the given request packet type. + /// </summary> + /// <param name="requestPacketType">The request packet type whose response packet type to fetch.</param> + /// <returns>The response packet type, <c>null</c> if no type is mapped.</returns> + internal static Type? GetResponsePacketType(Type requestPacketType) + { + return requestToResponseMap.TryGetValue(requestPacketType, out Type responsePacketType) ? responsePacketType : default; + } + + /// <summary> + /// Rebuilds the packet registry, by registering every <see cref="IPacket"/> inheritor in the given assemblies. + /// </summary> + /// <param name="packetSourceAssemblies"> + /// The assemblies from which the packet types to register are sourced. + /// </param> + internal static void RegisterPacketSourceAssemblies(params Assembly[] packetSourceAssemblies) + { + foreach (Assembly assembly in packetSourceAssemblies) + { + RegisterPacketSourceAssembly(assembly); + } + } + + /// <summary> + /// Registers all the <see cref="IPacket"/> implementors in the given assembly. + /// </summary> + /// <param name="packetSourceAssembly">The assembly whose packet types to register.</param> + //[MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static void RegisterPacketSourceAssembly(Assembly packetSourceAssembly) + { + Dictionary<Type, Type?> requestToResponseTypeMap = new Dictionary<Type, Type?>(); + + foreach (Type type in packetSourceAssembly.DefinedTypes) + { + foreach (Type interfaceType in type.GetInterfaces()) + { + if (!typeof(IPacket).IsAssignableFrom(interfaceType) || interfaceType == typeof(IPacket)) + { + continue; + } + + if (interfaceType == typeof(IRequestPacket)) + { + requestToResponseTypeMap[type] = default; + } + else //if (interfaceType == typeof(IResponsePacket<>)) + { + Type handledRequestType = interfaceType.GetGenericArguments()[0]; + + requestToResponseTypeMap[handledRequestType] = type; + } + } + } + + RegisterPacketTypes(requestToResponseTypeMap); + } + + /// <summary> + /// Registers the given packet type to the registry. + /// </summary> + /// <param name="requestPacketType">The request packet type to register, if it is not registered.</param> + /// <param name="responsePacketType">The response packet associated with the request packet.</param> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static void RegisterPacketType(Type requestPacketType, Type? responsePacketType) + { + if (!idToPacketTypeMap.ContainsValue(requestPacketType)) + { + uint requestPacketTypeId = GetNewPacketTypeId(requestPacketType); + + idToPacketTypeMap.TrySetValue(requestPacketTypeId, requestPacketType); + } + + // if the given response packet is null, then skip registering a response packet type + if (responsePacketType == default) return; + + if (!idToPacketTypeMap.ContainsValue(responsePacketType)) + { + uint responsePacketTypeId = GetNewPacketTypeId(responsePacketType); + + idToPacketTypeMap.TrySetValue(responsePacketTypeId, responsePacketType); + } + + if (!requestToResponseMap.ContainsValue(requestPacketType)) + { + requestToResponseMap.TrySetValue(requestPacketType, responsePacketType); + } + } + + /// <summary> + /// Registers the given packet types to the registry. + /// </summary> + /// <param name="requestToResponsePacketTypeMap"> + /// The dictionary mapping the request packet types to register, to their relevant response packet types. + /// The response packet type can be null; then the request packet type is treated as a 'simple' packet. + /// </param> + internal static void RegisterPacketTypes(Dictionary<Type, Type?> requestToResponsePacketTypeMap) + { + foreach ((Type requestPacketType, Type? responsePacketType) in requestToResponsePacketTypeMap) + { + RegisterPacketType(requestPacketType, responsePacketType); + } + } + + /// <summary> + /// Initialises a new instance of the <see cref="PacketRegistry"/> class. + /// </summary> + static PacketRegistry() + { + idToPacketTypeMap = new BiDictionary<uint, Type>(); + + requestToResponseMap = new BiDictionary<Type, Type>(); + + RegisterPacketSourceAssembly(LibraryAssembly); + } + } +} +\ No newline at end of file diff --git a/NetSharp/NetSharp/Deprecated/PacketTypeIdAttribute.cs b/NetSharp/NetSharp/Deprecated/PacketTypeIdAttribute.cs @@ -0,0 +1,26 @@ +using System; + +namespace NetSharp.Deprecated +{ + /// <summary> + /// Allows the placing of a custom packet type on a class or struct. This is used if the class or struct + /// inherits from <see cref="IRequestPacket"/> or <see cref="IResponsePacket{TReq}"/>. + /// </summary> + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)] + internal sealed class PacketTypeIdAttribute : Attribute + { + /// <summary> + /// Initialises a new instance of the <see cref="PacketTypeIdAttribute"/> attribute. + /// </summary> + /// <param name="type">The custom type id that the decorated packet type should have.</param> + internal PacketTypeIdAttribute(uint type) + { + Id = type; + } + + /// <summary> + /// The custom type id that the decorated packet type should have. This overrides the automatically generated id. + /// </summary> + internal uint Id { get; } + } +} +\ No newline at end of file diff --git a/NetSharp/NetSharp/Deprecated/RemoteSocketClient.cs b/NetSharp/NetSharp/Deprecated/RemoteSocketClient.cs @@ -0,0 +1,73 @@ +using Microsoft.Extensions.ObjectPool; + +using NetSharp.Utils; + +using System; +using System.Net; +using System.Net.Sockets; +using System.Threading; +using System.Threading.Tasks; + +namespace NetSharp.Deprecated +{ + public class RemoteSocketClient : IDisposable + { + private readonly ObjectPool<SocketAsyncEventArgs> transmissionArgsPool; + + protected readonly Socket transmitterSocket; + + internal RemoteSocketClient(Socket clientSocket) + { + transmitterSocket = clientSocket; + + transmissionArgsPool = new DefaultObjectPool<SocketAsyncEventArgs>(new DefaultPooledObjectPolicy<SocketAsyncEventArgs>()); + } + + /// <inheritdoc /> + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// <summary> + /// Implementation of dispose pattern. + /// </summary> + /// <param name="disposing"> + /// Whether this method is being called by the object finalizer, or by the <see cref="Dispose()"/> method. + /// </param> + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + transmitterSocket.Dispose(); + } + } + + public async ValueTask<TransmissionResult> ReceiveAsync(EndPoint remoteEndPoint, SocketFlags receiveFlags, Memory<byte> receiveBuffer, + CancellationToken cancellationToken = default) + { + SocketAsyncEventArgs transmissionArgs = transmissionArgsPool.Get(); + + TransmissionResult receiveResult = await SocketOperations.ReceiveFromAsync(transmissionArgs, transmitterSocket, + remoteEndPoint, receiveFlags, receiveBuffer, cancellationToken); + + transmissionArgsPool.Return(transmissionArgs); + + return receiveResult; + } + + public async ValueTask<TransmissionResult> SendAsync(EndPoint remoteEndPoint, SocketFlags sendFlags, Memory<byte> sendBuffer, + CancellationToken cancellationToken = default) + { + SocketAsyncEventArgs transmissionArgs = transmissionArgsPool.Get(); + + TransmissionResult sendResult = await SocketOperations.SendToAsync(transmissionArgs, transmitterSocket, + remoteEndPoint, sendFlags, sendBuffer, cancellationToken); + + transmissionArgsPool.Return(transmissionArgs); + + return sendResult; + } + } +} +\ No newline at end of file diff --git a/NetSharp/NetSharp/Deprecated/RingBuffer.cs b/NetSharp/NetSharp/Deprecated/RingBuffer.cs @@ -0,0 +1,48 @@ +namespace NetSharp.Deprecated +{ + public class RingBuffer<T> + { + private readonly T[] buffer; + + private int currentIndex; + + public RingBuffer(int capacity) + { + buffer = new T[capacity]; + + Capacity = capacity; + Count = 0; + } + + public int Capacity { get; } + + public int Count { get; } + + public T Pop() + { + T removedItem = buffer[currentIndex--]; + + currentIndex = currentIndex < 0 ? currentIndex + Capacity : currentIndex; + + return removedItem; + } + + public bool Push(T newItem, out T removedItem) + { + bool overwroteItem = false; + removedItem = default; + + if (buffer[currentIndex] != null) + { + removedItem = buffer[currentIndex]; + overwroteItem = true; + } + + buffer[currentIndex] = newItem; + + currentIndex = (currentIndex + 1) % Capacity; + + return overwroteItem; + } + } +} +\ No newline at end of file diff --git a/NetSharp/NetSharp/Deprecated/SerialisedPacket.cs b/NetSharp/NetSharp/Deprecated/SerialisedPacket.cs @@ -1,5 +1,4 @@ using System; -using NetSharp.Packets; namespace NetSharp.Deprecated { diff --git a/NetSharp/NetSharp/Deprecated/Server.cs b/NetSharp/NetSharp/Deprecated/Server.cs @@ -1,12 +1,12 @@ -using System; +using NetSharp.Deprecated.Builtin; + +using System; using System.Collections.Concurrent; using System.Net; using System.Net.Sockets; using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; -using NetSharp.Packets; -using NetSharp.Packets.Builtin; namespace NetSharp.Deprecated { diff --git a/NetSharp/NetSharp/Deprecated/ServerClientConnection.cs b/NetSharp/NetSharp/Deprecated/ServerClientConnection.cs @@ -2,8 +2,6 @@ using System.IO; using System.Net; using System.Runtime.CompilerServices; -using NetSharp.Logging; -using NetSharp.Utils; namespace NetSharp.Deprecated { diff --git a/NetSharp/NetSharp/Deprecated/ServerExtensions.cs b/NetSharp/NetSharp/Deprecated/ServerExtensions.cs @@ -1,6 +1,5 @@ using System.Net; using System.Threading.Tasks; -using NetSharp.Utils; namespace NetSharp.Deprecated { diff --git a/NetSharp/NetSharp/Deprecated/SocketAcceptor.cs b/NetSharp/NetSharp/Deprecated/SocketAcceptor.cs @@ -0,0 +1,281 @@ +using Microsoft.Extensions.ObjectPool; + +using System; +using System.Net; +using System.Net.Sockets; +using System.Threading; +using System.Threading.Tasks; + +namespace NetSharp.Deprecated +{ + /// <summary> + /// Helper class providing awaitable wrappers around asynchronous Accept, Connect, and Disconnect operations. + /// </summary> + public sealed class SocketAcceptor + { + private readonly ObjectPool<SocketAsyncEventArgs> acceptAsyncEventArgsPool; + private readonly ObjectPool<SocketAsyncEventArgs> connectAsyncEventArgsPool; + private readonly ObjectPool<SocketAsyncEventArgs> disconnectAsyncEventArgsPool; + + private void HandleIOCompleted(object? sender, SocketAsyncEventArgs args) + { + switch (args.LastOperation) + { + case SocketAsyncOperation.Accept: + AsyncAcceptToken asyncAcceptToken = (AsyncAcceptToken)args.UserToken; + + if (asyncAcceptToken.CancellationToken.IsCancellationRequested) + { + asyncAcceptToken.CompletionSource.SetCanceled(); + } + else + { + if (args.SocketError != SocketError.Success) + { + asyncAcceptToken.CompletionSource.SetException( + new SocketException((int)args.SocketError)); + } + else + { + asyncAcceptToken.CompletionSource.SetResult(args.AcceptSocket); + } + } + + acceptAsyncEventArgsPool.Return(args); + + break; + + case SocketAsyncOperation.Connect: + AsyncConnectToken asyncConnectToken = (AsyncConnectToken)args.UserToken; + + if (asyncConnectToken.CancellationToken.IsCancellationRequested) + { + asyncConnectToken.CompletionSource.SetCanceled(); + } + else + { + if (args.SocketError != SocketError.Success) + { + asyncConnectToken.CompletionSource.SetException( + new SocketException((int)args.SocketError)); + } + else + { + asyncConnectToken.CompletionSource.SetResult(true); + } + } + + connectAsyncEventArgsPool.Return(args); + + break; + + case SocketAsyncOperation.Disconnect: + AsyncDisconnectToken asyncDisconnectToken = (AsyncDisconnectToken)args.UserToken; + + if (asyncDisconnectToken.CancellationToken.IsCancellationRequested) + { + asyncDisconnectToken.CompletionSource.SetCanceled(); + } + else + { + if (args.SocketError != SocketError.Success) + { + asyncDisconnectToken.CompletionSource.SetException( + new SocketException((int)args.SocketError)); + } + else + { + asyncDisconnectToken.CompletionSource.SetResult(true); + } + } + + disconnectAsyncEventArgsPool.Return(args); + + break; + + default: + throw new InvalidOperationException( + $"The {nameof(SocketAcceptor)} class doesn't support the {args.LastOperation} operation."); + } + } + + private readonly struct AsyncAcceptToken + { + public readonly CancellationToken CancellationToken; + public readonly TaskCompletionSource<Socket> CompletionSource; + + public AsyncAcceptToken(TaskCompletionSource<Socket> tcs, CancellationToken cancellationToken = default) + { + CompletionSource = tcs; + CancellationToken = cancellationToken; + } + } + + private readonly struct AsyncConnectToken + { + public readonly CancellationToken CancellationToken; + public readonly TaskCompletionSource<bool> CompletionSource; + + public AsyncConnectToken(TaskCompletionSource<bool> tcs, CancellationToken cancellationToken = default) + { + CompletionSource = tcs; + CancellationToken = cancellationToken; + } + } + + private readonly struct AsyncDisconnectToken + { + public readonly CancellationToken CancellationToken; + public readonly TaskCompletionSource<bool> CompletionSource; + + public AsyncDisconnectToken(TaskCompletionSource<bool> tcs, CancellationToken cancellationToken = default) + { + CompletionSource = tcs; + CancellationToken = cancellationToken; + } + } + + internal SocketAcceptor(int maxPooledObjects = 10) + { + acceptAsyncEventArgsPool = + new DefaultObjectPool<SocketAsyncEventArgs>(new DefaultPooledObjectPolicy<SocketAsyncEventArgs>(), + maxPooledObjects); + + connectAsyncEventArgsPool = + new DefaultObjectPool<SocketAsyncEventArgs>(new DefaultPooledObjectPolicy<SocketAsyncEventArgs>(), + maxPooledObjects); + + disconnectAsyncEventArgsPool = + new DefaultObjectPool<SocketAsyncEventArgs>(new DefaultPooledObjectPolicy<SocketAsyncEventArgs>(), + maxPooledObjects); + + for (int i = 0; i < maxPooledObjects; i++) + { + SocketAsyncEventArgs acceptArgs = new SocketAsyncEventArgs(); + acceptArgs.Completed += HandleIOCompleted; + acceptAsyncEventArgsPool.Return(acceptArgs); + + SocketAsyncEventArgs connectArgs = new SocketAsyncEventArgs(); + connectArgs.Completed += HandleIOCompleted; + connectAsyncEventArgsPool.Return(connectArgs); + + SocketAsyncEventArgs disconnectArgs = new SocketAsyncEventArgs(); + disconnectArgs.Completed += HandleIOCompleted; + connectAsyncEventArgsPool.Return(disconnectArgs); + } + } + + /// <summary> + /// Provides an awaitable wrapper around an asynchronous socket accept operation. + /// </summary> + /// <param name="socket">The socket which should be used to accept an incoming connection attempt.</param> + /// <param name="cancellationToken">The cancellation token to observe for the operation.</param> + /// <returns>The accepted socket.</returns> + public Task<Socket> AcceptAsync(Socket socket, CancellationToken cancellationToken = default) + { + TaskCompletionSource<Socket> tcs = new TaskCompletionSource<Socket>(); + + SocketAsyncEventArgs args = acceptAsyncEventArgsPool.Get(); + args.UserToken = new AsyncAcceptToken(tcs, cancellationToken); + + /* + // register cleanup action for when the cancellation token is thrown + cancellationToken.Register(() => + { + tcs.SetCanceled(); + + //TODO this is probably a hideous solution. find a better one + args.Completed -= HandleIOCompleted; + args.Dispose(); + + SocketAsyncEventArgs newArgs = new SocketAsyncEventArgs(); + newArgs.Completed += HandleIOCompleted; + acceptAsyncEventArgsPool.Return(newArgs); + }); + */ + + // if the accept operation doesn't complete synchronously, return the awaitable task + if (socket.AcceptAsync(args)) return tcs.Task; + + Socket result = args.AcceptSocket; + + acceptAsyncEventArgsPool.Return(args); + + return Task.FromResult(result); + } + + /// <summary> + /// Provides an awaitable wrapper around an asynchronous socket connect operation. + /// </summary> + /// <param name="socket">The socket which should asynchronously connect to the remote endpoint.</param> + /// <param name="remoteEndPoint">The remote endpoint to which the socket should connect.</param> + /// <param name="cancellationToken">The cancellation token to observe for the operation.</param> + public Task ConnectAsync(Socket socket, EndPoint remoteEndPoint, CancellationToken cancellationToken = default) + { + TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>(); + + SocketAsyncEventArgs args = connectAsyncEventArgsPool.Get(); + args.RemoteEndPoint = remoteEndPoint; + args.UserToken = new AsyncConnectToken(tcs, cancellationToken); + + /* + // register cleanup action for when the cancellation token is thrown + cancellationToken.Register(() => + { + tcs.SetCanceled(); + + //TODO this is probably a hideous solution. find a better one + args.Completed -= HandleIOCompleted; + args.Dispose(); + + SocketAsyncEventArgs newArgs = new SocketAsyncEventArgs(); + newArgs.Completed += HandleIOCompleted; + connectAsyncEventArgsPool.Return(newArgs); + }); + */ + + // if the connect operation doesn't complete synchronously, return the awaitable task + if (socket.ConnectAsync(args)) return tcs.Task; + + connectAsyncEventArgsPool.Return(args); + + return Task.CompletedTask; + } + + /// <summary> + /// Provides an awaitable wrapper around an asynchronous socket disconnect operation. + /// </summary> + /// <param name="socket">The socket which should asynchronously disconnect from its remote endpoint.</param> + /// <param name="cancellationToken">The cancellation token to observe for the operation.</param> + public Task DisconnectAsync(Socket socket, CancellationToken cancellationToken = default) + { + TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>(); + + SocketAsyncEventArgs args = connectAsyncEventArgsPool.Get(); + args.UserToken = new AsyncDisconnectToken(tcs, cancellationToken); + + /* + // register cleanup action for when the cancellation token is thrown + cancellationToken.Register(() => + { + tcs.SetCanceled(); + + //TODO this is probably a hideous solution. find a better one + args.Completed -= HandleIOCompleted; + args.Dispose(); + + SocketAsyncEventArgs newArgs = new SocketAsyncEventArgs(); + newArgs.Completed += HandleIOCompleted; + disconnectAsyncEventArgsPool.Return(newArgs); + }); + */ + + // if the disconnect operation doesn't complete synchronously, return the awaitable task + if (socket.DisconnectAsync(args)) return tcs.Task; + + connectAsyncEventArgsPool.Return(args); + + return Task.CompletedTask; + } + } +} +\ No newline at end of file diff --git a/NetSharp/NetSharp/Deprecated/SocketClient.cs b/NetSharp/NetSharp/Deprecated/SocketClient.cs @@ -0,0 +1,132 @@ +using Microsoft.Extensions.ObjectPool; + +using NetSharp.Utils; + +using System; +using System.Net; +using System.Net.Sockets; +using System.Threading; +using System.Threading.Tasks; + +namespace NetSharp.Deprecated +{ + public class SocketClient : IDisposable + { + private readonly ObjectPool<SocketAsyncEventArgs> transmissionArgsPool; + + /// <summary> + /// Destroys a socket client instance. + /// </summary> + ~SocketClient() + { + Dispose(false); + } + + protected readonly Socket transmitterSocket; + + /// <summary> + /// Implementation of dispose pattern. + /// </summary> + /// <param name="disposing"> + /// Whether this method is being called by the object finalizer, or by the <see cref="Dispose()"/> method. + /// </param> + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + transmitterSocket.Dispose(); + } + } + + public SocketClient(AddressFamily transmitterAddressFamily, SocketType transmitterSocketType, + ProtocolType transmitterProtocolType) + { + transmitterSocket = new Socket(transmitterAddressFamily, transmitterSocketType, transmitterProtocolType); + + transmissionArgsPool = new DefaultObjectPool<SocketAsyncEventArgs>(new DefaultPooledObjectPolicy<SocketAsyncEventArgs>()); + } + + /// <inheritdoc /> + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + public async ValueTask<TransmissionResult> ReceiveAsync(EndPoint remoteEndPoint, SocketFlags receiveFlags, Memory<byte> receiveBuffer, + CancellationToken cancellationToken = default) + { + SocketAsyncEventArgs transmissionArgs = transmissionArgsPool.Get(); + + TransmissionResult receiveResult = + await SocketOperations.ReceiveFromAsync(transmissionArgs, transmitterSocket, + remoteEndPoint, receiveFlags, receiveBuffer, cancellationToken).ConfigureAwait(false); + + transmissionArgsPool.Return(transmissionArgs); + + return receiveResult; + } + + public async ValueTask<TransmissionResult> SendAsync(EndPoint remoteEndPoint, SocketFlags sendFlags, Memory<byte> sendBuffer, + CancellationToken cancellationToken = default) + { + SocketAsyncEventArgs transmissionArgs = transmissionArgsPool.Get(); + + TransmissionResult sendResult = + await SocketOperations.SendToAsync(transmissionArgs, transmitterSocket, + remoteEndPoint, sendFlags, sendBuffer, cancellationToken).ConfigureAwait(false); + + transmissionArgsPool.Return(transmissionArgs); + + return sendResult; + } + + public Task<bool> TryBindAsync(EndPoint localEndPoint, TimeSpan timeout) + { + using CancellationTokenSource cts = new CancellationTokenSource(timeout); + + try + { + return Task.Run(() => + { + transmitterSocket.Bind(localEndPoint); + + return true; + }, cts.Token); + } + catch (TaskCanceledException) + { + return Task.FromResult(false); + } + catch (SocketException ex) + { + Console.WriteLine($"Socket exception on binding socket to {localEndPoint}: {ex}"); + return Task.FromResult(false); + } + } + + public Task<bool> TryConnectAsync(EndPoint remoteEndPoint, TimeSpan timeout) + { + using CancellationTokenSource cts = new CancellationTokenSource(timeout); + + try + { + return Task.Run(() => + { + transmitterSocket.Connect(remoteEndPoint); + + return true; + }, cts.Token); + } + catch (TaskCanceledException) + { + return Task.FromResult(false); + } + catch (SocketException ex) + { + Console.WriteLine($"Socket exception on connecting to {remoteEndPoint}: {ex}"); + return Task.FromResult(false); + } + } + } +} +\ No newline at end of file diff --git a/NetSharp/NetSharp/Deprecated/SocketOperationTokens.cs b/NetSharp/NetSharp/Deprecated/SocketOperationTokens.cs @@ -0,0 +1,80 @@ +using NetSharp.Utils; + +using System.Net.Sockets; +using System.Threading; +using System.Threading.Tasks; + +namespace NetSharp.Deprecated +{ + internal readonly struct AsyncAcceptToken + { + public readonly CancellationToken CancellationToken; + public readonly TaskCompletionSource<Socket> CompletionSource; + + public AsyncAcceptToken(in TaskCompletionSource<Socket> tcs, in CancellationToken cancellationToken = default) + { + CompletionSource = tcs; + CancellationToken = cancellationToken; + } + } + + internal readonly struct AsyncConnectToken + { + public readonly CancellationToken CancellationToken; + public readonly TaskCompletionSource<Socket> CompletionSource; + + public AsyncConnectToken(in TaskCompletionSource<Socket> tcs, in CancellationToken cancellationToken = default) + { + CompletionSource = tcs; + CancellationToken = cancellationToken; + } + } + + internal readonly struct AsyncDisconnectToken + { + public readonly CancellationToken CancellationToken; + public readonly TaskCompletionSource<bool> CompletionSource; + + public AsyncDisconnectToken(in TaskCompletionSource<bool> tcs, in CancellationToken cancellationToken = default) + { + CompletionSource = tcs; + CancellationToken = cancellationToken; + } + } + + internal readonly struct AsyncReadToken + { + public readonly CancellationToken CancellationToken; + public readonly TaskCompletionSource<TransmissionResult> CompletionSource; + + public AsyncReadToken(in TaskCompletionSource<TransmissionResult> tcs, in CancellationToken cancellationToken = default) + { + CompletionSource = tcs; + CancellationToken = cancellationToken; + } + } + + internal readonly struct AsyncWriteToken + { + public readonly CancellationToken CancellationToken; + public readonly TaskCompletionSource<TransmissionResult> CompletionSource; + + public AsyncWriteToken(in TaskCompletionSource<TransmissionResult> tcs, in CancellationToken cancellationToken = default) + { + CompletionSource = tcs; + CancellationToken = cancellationToken; + } + } + + internal readonly struct AsyncReadFromToken + { + public readonly CancellationToken CancellationToken; + public readonly TaskCompletionSource<TransmissionResult> CompletionSource; + + public AsyncReadFromToken(in TaskCompletionSource<TransmissionResult> tcs, in CancellationToken cancellationToken = default) + { + CompletionSource = tcs; + CancellationToken = cancellationToken; + } + } +} +\ No newline at end of file diff --git a/NetSharp/NetSharp/Deprecated/SocketOperations.cs b/NetSharp/NetSharp/Deprecated/SocketOperations.cs @@ -0,0 +1,469 @@ +using NetSharp.Utils; + +using System; +using System.Net; +using System.Net.Sockets; +using System.Threading; +using System.Threading.Tasks; + +namespace NetSharp.Deprecated +{ + /// <summary> + /// Provides helper awaitable functions for wrapping the <see cref="SocketAsyncEventArgs"/> pattern. + /// </summary> + public static class SocketOperations + { + private static void HandleIOCompleted(object? sender, SocketAsyncEventArgs args) + { + args.Completed -= HandleIOCompleted; + + switch (args.LastOperation) + { + case SocketAsyncOperation.Receive: + AsyncReadToken asyncReceiveToken = (AsyncReadToken)args.UserToken; + + if (asyncReceiveToken.CancellationToken.IsCancellationRequested) + { + asyncReceiveToken.CompletionSource.SetCanceled(); + } + else + { + if (args.SocketError != SocketError.Success) + { + asyncReceiveToken.CompletionSource.SetException( + new SocketException((int)args.SocketError)); + } + else if (args.BytesTransferred > 0) + { + TransmissionResult result = new TransmissionResult(args); + + asyncReceiveToken.CompletionSource.SetResult(result); + } + else + { + asyncReceiveToken.CompletionSource.SetException( + new Exception($"Receive method received 0 bytes from remote endpoint!")); + } + } + + break; + + case SocketAsyncOperation.ReceiveFrom: + AsyncReadFromToken asyncReceiveFromToken = (AsyncReadFromToken)args.UserToken; + + if (asyncReceiveFromToken.CancellationToken.IsCancellationRequested) + { + asyncReceiveFromToken.CompletionSource.SetCanceled(); + } + else + { + if (args.SocketError != SocketError.Success) + { + asyncReceiveFromToken.CompletionSource.SetException( + new SocketException((int)args.SocketError)); + } + else + { + TransmissionResult result = new TransmissionResult(args); + + asyncReceiveFromToken.CompletionSource.SetResult(result); + } + } + + break; + + case SocketAsyncOperation.Send: + AsyncWriteToken asyncSendToken = (AsyncWriteToken)args.UserToken; + + if (asyncSendToken.CancellationToken.IsCancellationRequested) + { + asyncSendToken.CompletionSource.SetCanceled(); + } + else + { + if (args.SocketError != SocketError.Success) + { + asyncSendToken.CompletionSource.SetException( + new SocketException((int)args.SocketError)); + } + else + { + TransmissionResult result = new TransmissionResult(args); + + asyncSendToken.CompletionSource.SetResult(result); + } + } + + break; + + case SocketAsyncOperation.SendTo: + AsyncWriteToToken asyncSendToToken = (AsyncWriteToToken)args.UserToken; + + if (asyncSendToToken.CancellationToken.IsCancellationRequested) + { + asyncSendToToken.CompletionSource.SetCanceled(); + } + else + { + if (args.SocketError != SocketError.Success) + { + asyncSendToToken.CompletionSource.SetException( + new SocketException((int)args.SocketError)); + } + else + { + TransmissionResult result = new TransmissionResult(args); + + asyncSendToToken.CompletionSource.SetResult(result); + } + } + + break; + + case SocketAsyncOperation.Accept: + AsyncAcceptToken asyncAcceptToken = (AsyncAcceptToken)args.UserToken; + + if (asyncAcceptToken.CancellationToken.IsCancellationRequested) + { + asyncAcceptToken.CompletionSource.SetCanceled(); + } + else + { + if (args.SocketError != SocketError.Success) + { + asyncAcceptToken.CompletionSource.SetException( + new SocketException((int)args.SocketError)); + } + else + { + asyncAcceptToken.CompletionSource.SetResult(args.AcceptSocket); + } + } + + break; + + case SocketAsyncOperation.Connect: + AsyncConnectToken asyncConnectToken = (AsyncConnectToken)args.UserToken; + + if (asyncConnectToken.CancellationToken.IsCancellationRequested) + { + asyncConnectToken.CompletionSource.SetCanceled(); + } + else + { + if (args.SocketError != SocketError.Success) + { + asyncConnectToken.CompletionSource.SetException( + new SocketException((int)args.SocketError)); + } + else + { + asyncConnectToken.CompletionSource.SetResult(args.ConnectSocket); + } + } + + break; + + case SocketAsyncOperation.Disconnect: + AsyncDisconnectToken asyncDisconnectToken = (AsyncDisconnectToken)args.UserToken; + + if (asyncDisconnectToken.CancellationToken.IsCancellationRequested) + { + asyncDisconnectToken.CompletionSource.SetCanceled(); + } + else + { + if (args.SocketError != SocketError.Success) + { + asyncDisconnectToken.CompletionSource.SetException( + new SocketException((int)args.SocketError)); + } + else + { + asyncDisconnectToken.CompletionSource.SetResult(true); + } + } + + break; + + default: + throw new InvalidOperationException( + $"{nameof(SocketOperations)} doesn't support the {args.LastOperation} operation."); + } + } + + private readonly struct AsyncWriteToToken + { + public readonly CancellationToken CancellationToken; + public readonly TaskCompletionSource<TransmissionResult> CompletionSource; + + public AsyncWriteToToken(in TaskCompletionSource<TransmissionResult> tcs, in CancellationToken cancellationToken = default) + { + CompletionSource = tcs; + CancellationToken = cancellationToken; + } + } + + public static ValueTask<Socket> AcceptAsync(SocketAsyncEventArgs clientAcceptArgs, Socket socket, + CancellationToken cancellationToken = default) + { + TaskCompletionSource<Socket> tcs = new TaskCompletionSource<Socket>(); + + clientAcceptArgs.UserToken = new AsyncAcceptToken(tcs, cancellationToken); + + clientAcceptArgs.Completed += HandleIOCompleted; + + /* + // register cleanup action for when the cancellation token is thrown + cancellationToken.Register(() => + { + tcs.SetCanceled(); + + //TODO this is probably a hideous solution. find a better one + args.Completed -= HandleIOCompleted; + args.Dispose(); + + SocketAsyncEventArgs newArgs = new SocketAsyncEventArgs(); + newArgs.Completed += HandleIOCompleted; + acceptAsyncEventArgsPool.Return(newArgs); + }); + */ + + // if the accept operation doesn't complete synchronously, return the awaitable task + if (socket.AcceptAsync(clientAcceptArgs)) return new ValueTask<Socket>(tcs.Task); + + Socket result = clientAcceptArgs.AcceptSocket; + clientAcceptArgs.Completed -= HandleIOCompleted; + + return new ValueTask<Socket>(result); + } + + public static ValueTask<Socket> ConnectAsync(SocketAsyncEventArgs clientConnectArgs, Socket socket, EndPoint remoteEndPoint, + CancellationToken cancellationToken = default) + { + TaskCompletionSource<Socket> tcs = new TaskCompletionSource<Socket>(); + + clientConnectArgs.RemoteEndPoint = remoteEndPoint; + clientConnectArgs.UserToken = new AsyncConnectToken(tcs, cancellationToken); + + clientConnectArgs.Completed += HandleIOCompleted; + + /* + // register cleanup action for when the cancellation token is thrown + cancellationToken.Register(() => + { + tcs.SetCanceled(); + + //TODO this is probably a hideous solution. find a better one + args.Completed -= HandleIOCompleted; + args.Dispose(); + + SocketAsyncEventArgs newArgs = new SocketAsyncEventArgs(); + newArgs.Completed += HandleIOCompleted; + connectAsyncEventArgsPool.Return(newArgs); + }); + */ + + // if the connect operation doesn't complete synchronously, return the awaitable task + if (socket.ConnectAsync(clientConnectArgs)) return new ValueTask<Socket>(tcs.Task); + + Socket result = clientConnectArgs.ConnectSocket; + clientConnectArgs.Completed -= HandleIOCompleted; + + return new ValueTask<Socket>(result); + } + + public static ValueTask DisconnectAsync(SocketAsyncEventArgs clientDisconnectArgs, Socket socket, + CancellationToken cancellationToken = default) + { + TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>(); + + clientDisconnectArgs.UserToken = new AsyncDisconnectToken(tcs, cancellationToken); + + clientDisconnectArgs.Completed += HandleIOCompleted; + + /* + // register cleanup action for when the cancellation token is thrown + cancellationToken.Register(() => + { + tcs.SetCanceled(); + + //TODO this is probably a hideous solution. find a better one + args.Completed -= HandleIOCompleted; + args.Dispose(); + + SocketAsyncEventArgs newArgs = new SocketAsyncEventArgs(); + newArgs.Completed += HandleIOCompleted; + disconnectAsyncEventArgsPool.Return(newArgs); + }); + */ + + // if the disconnect operation doesn't complete synchronously, return the awaitable task + if (socket.DisconnectAsync(clientDisconnectArgs)) return new ValueTask(tcs.Task); + + clientDisconnectArgs.Completed -= HandleIOCompleted; + + return new ValueTask(); + } + + public static ValueTask<TransmissionResult> ReceiveAsync(SocketAsyncEventArgs socketArgs, Socket socket, EndPoint remoteEndPoint, + SocketFlags socketFlags, Memory<byte> inputBuffer, CancellationToken cancellationToken = default) + { + TaskCompletionSource<TransmissionResult> tcs = new TaskCompletionSource<TransmissionResult>(); + + socketArgs.SetBuffer(inputBuffer); + socketArgs.SocketFlags = socketFlags; + socketArgs.RemoteEndPoint = remoteEndPoint; + socketArgs.UserToken = new AsyncReadToken(tcs, cancellationToken); + + socketArgs.Completed += HandleIOCompleted; + + /* + // register cleanup action for when the cancellation token is thrown + cancellationToken.Register(() => + { + tcs.SetCanceled(); + + receiveBufferPool.Return(rentedReceiveFromBuffer, true); + + //TODO this is probably a hideous solution. find a better one + args.Completed -= HandleIOCompleted; + args.Dispose(); + + SocketAsyncEventArgs newArgs = new SocketAsyncEventArgs(); + newArgs.Completed += HandleIOCompleted; + receiveAsyncEventArgsPool.Return(newArgs); + }); + */ + + // if the receive operation doesn't complete synchronously, returns the awaitable task + if (socket.ReceiveAsync(socketArgs)) return new ValueTask<TransmissionResult>(tcs.Task); + + socketArgs.Completed -= HandleIOCompleted; + + TransmissionResult result = new TransmissionResult(socketArgs); + + return new ValueTask<TransmissionResult>(result); + } + + public static ValueTask<TransmissionResult> ReceiveFromAsync(SocketAsyncEventArgs socketArgs, Socket socket, EndPoint remoteEndPoint, + SocketFlags socketFlags, Memory<byte> inputBuffer, CancellationToken cancellationToken = default) + { + TaskCompletionSource<TransmissionResult> tcs = new TaskCompletionSource<TransmissionResult>(); + + socketArgs.SetBuffer(inputBuffer); + socketArgs.SocketFlags = socketFlags; + socketArgs.RemoteEndPoint = remoteEndPoint; + socketArgs.UserToken = new AsyncReadFromToken(tcs, cancellationToken); + + socketArgs.Completed += HandleIOCompleted; + + /* + // register cleanup action for when the cancellation token is thrown + cancellationToken.Register(() => + { + tcs.SetCanceled(); + + receiveBufferPool.Return(rentedReceiveFromBuffer, true); + + //TODO this is probably a hideous solution. find a better one + args.Completed -= HandleIOCompleted; + args.Dispose(); + + SocketAsyncEventArgs newArgs = new SocketAsyncEventArgs(); + newArgs.Completed += HandleIOCompleted; + receiveAsyncEventArgsPool.Return(newArgs); + }); + */ + + // if the receive operation doesn't complete synchronously, returns the awaitable task + if (socket.ReceiveFromAsync(socketArgs)) return new ValueTask<TransmissionResult>(tcs.Task); + + socketArgs.Completed -= HandleIOCompleted; + + TransmissionResult result = new TransmissionResult(socketArgs); + + return new ValueTask<TransmissionResult>(result); + } + + public static ValueTask<TransmissionResult> SendAsync(SocketAsyncEventArgs socketArgs, Socket socket, EndPoint remoteEndPoint, + SocketFlags socketFlags, Memory<byte> outputBuffer, CancellationToken cancellationToken = default) + { + TaskCompletionSource<TransmissionResult> tcs = new TaskCompletionSource<TransmissionResult>(); + + socketArgs.SetBuffer(outputBuffer); + socketArgs.SocketFlags = socketFlags; + socketArgs.RemoteEndPoint = remoteEndPoint; + socketArgs.UserToken = new AsyncWriteToken(tcs, cancellationToken); + + socketArgs.Completed += HandleIOCompleted; + + /* + // register cleanup action for when the cancellation token is thrown + cancellationToken.Register(() => + { + tcs.SetCanceled(); + + sendBufferPool.Return(rentedSendToBuffer, true); + + //TODO this is probably a hideous solution. find a better one + args.Completed -= HandleIOCompleted; + args.Dispose(); + + SocketAsyncEventArgs newArgs = new SocketAsyncEventArgs(); + newArgs.Completed += HandleIOCompleted; + sendAsyncEventArgsPool.Return(newArgs); + }); + */ + + // if the send operation doesn't complete synchronously, return the awaitable task + if (socket.SendAsync(socketArgs)) return new ValueTask<TransmissionResult>(tcs.Task); + + socketArgs.Completed -= HandleIOCompleted; + + TransmissionResult result = new TransmissionResult(socketArgs); + + return new ValueTask<TransmissionResult>(result); + } + + public static ValueTask<TransmissionResult> SendToAsync(SocketAsyncEventArgs socketArgs, Socket socket, EndPoint remoteEndPoint, + SocketFlags socketFlags, Memory<byte> outputBuffer, CancellationToken cancellationToken = default) + { + TaskCompletionSource<TransmissionResult> tcs = new TaskCompletionSource<TransmissionResult>(); + + socketArgs.SetBuffer(outputBuffer); + socketArgs.SocketFlags = socketFlags; + socketArgs.RemoteEndPoint = remoteEndPoint; + socketArgs.UserToken = new AsyncWriteToToken(tcs, cancellationToken); + + socketArgs.Completed += HandleIOCompleted; + + /* + // register cleanup action for when the cancellation token is thrown + cancellationToken.Register(() => + { + tcs.SetCanceled(); + + sendBufferPool.Return(rentedSendToBuffer, true); + + //TODO this is probably a hideous solution. find a better one + args.Completed -= HandleIOCompleted; + args.Dispose(); + + SocketAsyncEventArgs newArgs = new SocketAsyncEventArgs(); + newArgs.Completed += HandleIOCompleted; + sendAsyncEventArgsPool.Return(newArgs); + }); + */ + + // if the send operation doesn't complete synchronously, return the awaitable task + if (socket.SendToAsync(socketArgs)) return new ValueTask<TransmissionResult>(tcs.Task); + + socketArgs.Completed -= HandleIOCompleted; + + TransmissionResult result = new TransmissionResult(socketArgs); + + return new ValueTask<TransmissionResult>(result); + } + } +} +\ No newline at end of file diff --git a/NetSharp/NetSharp/Deprecated/SocketReader.cs b/NetSharp/NetSharp/Deprecated/SocketReader.cs @@ -0,0 +1,153 @@ +using Microsoft.Extensions.ObjectPool; + +using NetSharp.Utils; + +using System; +using System.Buffers; +using System.Net; +using System.Net.Sockets; +using System.Threading; +using System.Threading.Tasks; + +namespace NetSharp.Deprecated +{ + /// <summary> + /// Helper class providing awaitable wrappers around asynchronous Receive and ReceiveFrom operations. + /// </summary> + public sealed class SocketReader + { + private readonly int PacketBufferLength; + private readonly ObjectPool<SocketAsyncEventArgs> receiveFromAsyncEventArgsPool; + private readonly ArrayPool<byte> receiveFromBufferPool; + + private void HandleIOCompleted(object? sender, SocketAsyncEventArgs args) + { + switch (args.LastOperation) + { + case SocketAsyncOperation.ReceiveFrom: + AsyncReadToken asyncReceiveFromToken = (AsyncReadToken)args.UserToken; + + if (asyncReceiveFromToken.CancellationToken.IsCancellationRequested) + { + asyncReceiveFromToken.CompletionSource.SetCanceled(); + } + else + { + if (args.SocketError != SocketError.Success) + { + asyncReceiveFromToken.CompletionSource.SetException( + new SocketException((int)args.SocketError)); + } + else + { + args.MemoryBuffer.CopyTo(asyncReceiveFromToken.UserBuffer); + + TransmissionResult result = new TransmissionResult(args); + + asyncReceiveFromToken.CompletionSource.SetResult(result); + } + } + + receiveFromBufferPool.Return(asyncReceiveFromToken.RentedBuffer, true); + receiveFromAsyncEventArgsPool.Return(args); + + break; + + default: + throw new InvalidOperationException( + $"The {nameof(SocketReader)} class doesn't support the {args.LastOperation} operation."); + } + } + + private readonly struct AsyncReadToken + { + public readonly CancellationToken CancellationToken; + public readonly TaskCompletionSource<TransmissionResult> CompletionSource; + public readonly byte[] RentedBuffer; + public readonly Memory<byte> UserBuffer; + + public AsyncReadToken(byte[] rentedBuffer, Memory<byte> userBuffer, TaskCompletionSource<TransmissionResult> tcs, + CancellationToken cancellationToken = default) + { + RentedBuffer = rentedBuffer; + UserBuffer = userBuffer; + + CompletionSource = tcs; + CancellationToken = cancellationToken; + } + } + + internal SocketReader(int packetBufferLength = NetworkPacket.PacketSize, int maxPooledObjects = 10, + bool preallocateBuffers = false) + { + PacketBufferLength = packetBufferLength; + + receiveFromBufferPool = ArrayPool<byte>.Create(packetBufferLength, maxPooledObjects); + + receiveFromAsyncEventArgsPool = + new DefaultObjectPool<SocketAsyncEventArgs>(new DefaultPooledObjectPolicy<SocketAsyncEventArgs>(), + maxPooledObjects); + + for (int i = 0; i < maxPooledObjects; i++) + { + SocketAsyncEventArgs receiveFromArgs = new SocketAsyncEventArgs(); + receiveFromArgs.Completed += HandleIOCompleted; + receiveFromAsyncEventArgsPool.Return(receiveFromArgs); + } + } + + /// <summary> + /// Provides an awaitable wrapper around an asynchronous socket receive operation. + /// </summary> + /// <param name="socket">The socket which should receive data from the remote endpoint.</param> + /// <param name="remoteEndPoint">The remove endpoint from which data should be received.</param> + /// <param name="socketFlags">The socket flags associated with the receive operation.</param> + /// <param name="inputBuffer">The memory buffer into which received data will be stored.</param> + /// <param name="cancellationToken">The cancellation token to observe for the operation.</param> + /// <returns>The result of the receive operation.</returns> + public Task<TransmissionResult> ReceiveFromAsync(Socket socket, EndPoint remoteEndPoint, SocketFlags socketFlags, + Memory<byte> inputBuffer, CancellationToken cancellationToken = default) + { + TaskCompletionSource<TransmissionResult> tcs = new TaskCompletionSource<TransmissionResult>(); + + byte[] rentedReceiveFromBuffer = receiveFromBufferPool.Rent(PacketBufferLength); + Memory<byte> rentedReceiveFromBufferMemory = new Memory<byte>(rentedReceiveFromBuffer); + + SocketAsyncEventArgs args = receiveFromAsyncEventArgsPool.Get(); + args.SetBuffer(rentedReceiveFromBufferMemory); + args.SocketFlags = socketFlags; + args.RemoteEndPoint = remoteEndPoint; + args.UserToken = new AsyncReadToken(rentedReceiveFromBuffer, inputBuffer, tcs, cancellationToken); + + /* + // register cleanup action for when the cancellation token is thrown + cancellationToken.Register(() => + { + tcs.SetCanceled(); + + receiveBufferPool.Return(rentedReceiveFromBuffer, true); + + //TODO this is probably a hideous solution. find a better one + args.Completed -= HandleIOCompleted; + args.Dispose(); + + SocketAsyncEventArgs newArgs = new SocketAsyncEventArgs(); + newArgs.Completed += HandleIOCompleted; + receiveAsyncEventArgsPool.Return(newArgs); + }); + */ + + // if the receive operation doesn't complete synchronously, returns the awaitable task + if (socket.ReceiveFromAsync(args)) return tcs.Task; + + args.MemoryBuffer.CopyTo(inputBuffer); + + TransmissionResult result = new TransmissionResult(args); + + receiveFromBufferPool.Return(rentedReceiveFromBuffer, true); + receiveFromAsyncEventArgsPool.Return(args); + + return Task.FromResult(result); + } + } +} +\ No newline at end of file diff --git a/NetSharp/NetSharp/Deprecated/SocketServer.cs b/NetSharp/NetSharp/Deprecated/SocketServer.cs @@ -0,0 +1,121 @@ +using Microsoft.Extensions.ObjectPool; + +using NetSharp.Utils; + +using System; +using System.Net; +using System.Net.Sockets; +using System.Threading; +using System.Threading.Tasks; + +namespace NetSharp.Deprecated +{ + public class SocketServer : IDisposable + { + private readonly ObjectPool<SocketAsyncEventArgs> transmissionArgsPool; + + /// <summary> + /// Destroys a socket server instance. + /// </summary> + ~SocketServer() + { + Dispose(false); + } + + /// <summary> + /// The socket which should be used to listen for incoming data and to send outgoing data. + /// </summary> + protected readonly Socket listenerSocket; + + /// <summary> + /// Implementation of dispose pattern. + /// </summary> + /// <param name="disposing"> + /// Whether this method is being called by the object finalizer, or by the <see cref="Dispose()"/> method. + /// </param> + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + listenerSocket.Dispose(); + } + } + + public SocketServer(AddressFamily listenerAddressFamily, SocketType listenerSocketType, + ProtocolType listenerProtocolType) + { + listenerSocket = new Socket(listenerAddressFamily, listenerSocketType, listenerProtocolType); + + transmissionArgsPool = new DefaultObjectPool<SocketAsyncEventArgs>(new DefaultPooledObjectPolicy<SocketAsyncEventArgs>()); + } + + /// <inheritdoc /> + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + public async ValueTask<TransmissionResult> ReceiveAsync(EndPoint remoteEndPoint, SocketFlags receiveFlags, Memory<byte> receiveBuffer, + CancellationToken cancellationToken = default) + { + SocketAsyncEventArgs transmissionArgs = transmissionArgsPool.Get(); + + TransmissionResult receiveResult = + await SocketOperations.ReceiveFromAsync(transmissionArgs, listenerSocket, remoteEndPoint, receiveFlags, + receiveBuffer, cancellationToken).ConfigureAwait(false); + + transmissionArgsPool.Return(transmissionArgs); + + return receiveResult; + } + + public async ValueTask<TransmissionResult> SendAsync(EndPoint remoteEndPoint, SocketFlags sendFlags, Memory<byte> sendBuffer, + CancellationToken cancellationToken = default) + { + SocketAsyncEventArgs transmissionArgs = transmissionArgsPool.Get(); + + TransmissionResult sendResult = + await SocketOperations.SendToAsync(transmissionArgs, listenerSocket, remoteEndPoint, sendFlags, + sendBuffer, cancellationToken).ConfigureAwait(false); + + transmissionArgsPool.Return(transmissionArgs); + + return sendResult; + } + + public async Task<RemoteSocketClient> AcceptAsync(CancellationToken cancellationToken = default) + { + using SocketAsyncEventArgs clientAcceptArgs = new SocketAsyncEventArgs(); + + Socket clientSocket = await SocketOperations + .AcceptAsync(clientAcceptArgs, listenerSocket, cancellationToken).ConfigureAwait(false); + + return new RemoteSocketClient(clientSocket); + } + + public Task<bool> TryBindAsync(EndPoint localEndPoint, TimeSpan timeout) + { + using CancellationTokenSource cts = new CancellationTokenSource(timeout); + + try + { + return Task.Run(() => + { + listenerSocket.Bind(localEndPoint); + + return true; + }, cts.Token); + } + catch (TaskCanceledException) + { + return Task.FromResult(false); + } + catch (SocketException ex) + { + Console.WriteLine($"Socket exception on binding socket to {localEndPoint}: {ex}"); + return Task.FromResult(false); + } + } + } +} +\ No newline at end of file diff --git a/NetSharp/NetSharp/Deprecated/SocketWriter.cs b/NetSharp/NetSharp/Deprecated/SocketWriter.cs @@ -0,0 +1,144 @@ +using Microsoft.Extensions.ObjectPool; + +using System; +using System.Buffers; +using System.Net; +using System.Net.Sockets; +using System.Threading; +using System.Threading.Tasks; + +namespace NetSharp.Deprecated +{ + /// <summary> + /// Helper class providing awaitable wrappers around asynchronous Send and SendTo operations. + /// </summary> + public sealed class SocketWriter + { + private readonly int PacketBufferLength; + private readonly ObjectPool<SocketAsyncEventArgs> sendToAsyncEventArgsPool; + private readonly ArrayPool<byte> sendToBufferPool; + + private void HandleIOCompleted(object? sender, SocketAsyncEventArgs args) + { + switch (args.LastOperation) + { + case SocketAsyncOperation.SendTo: + AsyncWriteToken asyncSendToToken = (AsyncWriteToken)args.UserToken; + + if (asyncSendToToken.CancellationToken.IsCancellationRequested) + { + asyncSendToToken.CompletionSource.SetCanceled(); + } + else + { + if (args.SocketError != SocketError.Success) + { + asyncSendToToken.CompletionSource.SetException( + new SocketException((int)args.SocketError)); + } + else + { + asyncSendToToken.CompletionSource.SetResult(args.BytesTransferred); + } + } + + sendToBufferPool.Return(asyncSendToToken.RentedBuffer, true); + sendToAsyncEventArgsPool.Return(args); + break; + + default: + throw new InvalidOperationException( + $"The {nameof(SocketWriter)} class doesn't support the {args.LastOperation} operation."); + } + } + + private readonly struct AsyncWriteToken + { + public readonly CancellationToken CancellationToken; + public readonly TaskCompletionSource<int> CompletionSource; + public readonly byte[] RentedBuffer; + + public AsyncWriteToken(byte[] rentedBuffer, TaskCompletionSource<int> tcs, + CancellationToken cancellationToken = default) + { + RentedBuffer = rentedBuffer; + + CompletionSource = tcs; + CancellationToken = cancellationToken; + } + } + + internal SocketWriter(int packetBufferLength = NetworkPacket.PacketSize, int maxPooledObjects = 10, + bool preallocateBuffers = false) + { + PacketBufferLength = packetBufferLength; + + sendToBufferPool = ArrayPool<byte>.Create(packetBufferLength, maxPooledObjects); + + sendToAsyncEventArgsPool = + new DefaultObjectPool<SocketAsyncEventArgs>(new DefaultPooledObjectPolicy<SocketAsyncEventArgs>(), + maxPooledObjects); + + for (int i = 0; i < maxPooledObjects; i++) + { + SocketAsyncEventArgs sendToArgs = new SocketAsyncEventArgs(); + sendToArgs.Completed += HandleIOCompleted; + sendToAsyncEventArgsPool.Return(sendToArgs); + } + } + + /// <summary> + /// Provides an awaitable wrapper around an asynchronous socket send operation. + /// </summary> + /// <param name="socket">The socket which should send the data to the remote endpoint.</param> + /// <param name="remoteEndPoint">The remote endpoint to which data should be written.</param> + /// <param name="socketFlags">The socket flags associated with the send operation.</param> + /// <param name="outputBuffer">The data buffer which should be sent.</param> + /// <param name="cancellationToken">The cancellation token to observe for the operation.</param> + /// <returns>The number of bytes of data which were written to the remote endpoint.</returns> + public ValueTask<int> SendToAsync(Socket socket, EndPoint remoteEndPoint, SocketFlags socketFlags, + Memory<byte> outputBuffer, CancellationToken cancellationToken = default) + { + TaskCompletionSource<int> tcs = new TaskCompletionSource<int>(); + + byte[] rentedSendToBuffer = sendToBufferPool.Rent(PacketBufferLength); + Memory<byte> rentedSendToBufferMemory = new Memory<byte>(rentedSendToBuffer); + + outputBuffer.CopyTo(rentedSendToBufferMemory); + + SocketAsyncEventArgs args = sendToAsyncEventArgsPool.Get(); + args.SetBuffer(rentedSendToBufferMemory); + args.SocketFlags = socketFlags; + args.RemoteEndPoint = remoteEndPoint; + args.UserToken = new AsyncWriteToken(rentedSendToBuffer, tcs, cancellationToken); + + /* + // register cleanup action for when the cancellation token is thrown + cancellationToken.Register(() => + { + tcs.SetCanceled(); + + sendBufferPool.Return(rentedSendToBuffer, true); + + //TODO this is probably a hideous solution. find a better one + args.Completed -= HandleIOCompleted; + args.Dispose(); + + SocketAsyncEventArgs newArgs = new SocketAsyncEventArgs(); + newArgs.Completed += HandleIOCompleted; + sendAsyncEventArgsPool.Return(newArgs); + }); + */ + + // if the send operation doesn't complete synchronously, return the awaitable task + if (socket.SendToAsync(args)) return new ValueTask<int>(tcs.Task); + + int result = args.BytesTransferred; + + sendToBufferPool.Return(rentedSendToBuffer, true); + sendToAsyncEventArgsPool.Return(args); + + return new ValueTask<int>(result); + } + } +} +\ No newline at end of file diff --git a/NetSharp/NetSharp/Deprecated/TcpClient.cs b/NetSharp/NetSharp/Deprecated/TcpClient.cs @@ -1,8 +1,8 @@ -using System; +using NetSharp.Deprecated.Builtin; + +using System; using System.Net.Sockets; using System.Threading.Tasks; -using NetSharp.Packets; -using NetSharp.Packets.Builtin; namespace NetSharp.Deprecated { diff --git a/NetSharp/NetSharp/Deprecated/TcpServer.cs b/NetSharp/NetSharp/Deprecated/TcpServer.cs @@ -1,11 +1,11 @@ -using System; +using NetSharp.Deprecated.Builtin; + +using System; using System.Net; using System.Net.Sockets; using System.Text; using System.Threading; using System.Threading.Tasks; -using NetSharp.Packets; -using NetSharp.Packets.Builtin; namespace NetSharp.Deprecated { diff --git a/NetSharp/NetSharp/Deprecated/UdpClient.cs b/NetSharp/NetSharp/Deprecated/UdpClient.cs @@ -1,8 +1,8 @@ -using System; +using NetSharp.Deprecated.Builtin; + +using System; using System.Net.Sockets; using System.Threading.Tasks; -using NetSharp.Packets; -using NetSharp.Packets.Builtin; namespace NetSharp.Deprecated { diff --git a/NetSharp/NetSharp/Deprecated/UdpServer.cs b/NetSharp/NetSharp/Deprecated/UdpServer.cs @@ -1,4 +1,6 @@ -using System; +using NetSharp.Deprecated.Builtin; + +using System; using System.Collections.Concurrent; using System.Net; using System.Net.Sockets; @@ -6,8 +8,6 @@ using System.Text; using System.Threading; using System.Threading.Channels; using System.Threading.Tasks; -using NetSharp.Packets; -using NetSharp.Packets.Builtin; namespace NetSharp.Deprecated { diff --git a/NetSharp/NetSharp/Extensions/ConnectionBuilderExtensions.cs b/NetSharp/NetSharp/Extensions/ConnectionBuilderExtensions.cs @@ -1,29 +0,0 @@ -using System; -using System.IO; -using System.Net.Sockets; -using NetSharp.Logging; - -namespace NetSharp.Extensions -{ - /// <summary> - /// Provides additional methods and functionality to the <see cref="ConnectionBuilder"/> class. - /// </summary> - public static class ConnectionBuilderExtensions - { - public static ConnectionBuilder AppendIncomingPipelineStage(this ConnectionBuilder instance, - in Func<Memory<byte>, Memory<byte>> transform) - => instance.WithIncomingPipelineStage(transform, instance.IncomingPacketPipelineStageCount); - - public static ConnectionBuilder AppendOutgoingPipelineStage(this ConnectionBuilder instance, - in Func<Memory<byte>, Memory<byte>> transform) - => instance.WithOutgoingPipelineStage(transform, instance.OutgoingPacketPipelineStageCount); - - public static ConnectionBuilder WithLogging(this ConnectionBuilder instance, - Stream loggingStream, LogLevel minimumLogLevel) - => instance.WithLogging(new ConnectionBuilder.LoggingSettings(loggingStream, minimumLogLevel)); - - public static ConnectionBuilder WithPooling(this ConnectionBuilder instance, - int poolSize, bool preallocateBuffers) - => instance.WithPooling(new ConnectionBuilder.PoolingSettings(poolSize, preallocateBuffers)); - } -} -\ No newline at end of file diff --git a/NetSharp/NetSharp/Extensions/ConnectionExtensions.cs b/NetSharp/NetSharp/Extensions/ConnectionExtensions.cs @@ -1,72 +0,0 @@ -using System; -using System.Net; -using System.Net.Sockets; -using System.Threading; -using System.Threading.Tasks; -using NetSharp.Utils; - -namespace NetSharp.Extensions -{ - /// <summary> - /// Provides additional methods and functionality to the <see cref="Connection"/> class. - /// </summary> - public static class ConnectionExtensions - { - public static Task<TransmissionResult> ReceiveAsync(this Connection instance, - EndPoint remoteEndPoint, Memory<byte> inputBuffer, SocketFlags flags) - => instance.ReceiveAsync(inputBuffer, flags, Timeout.InfiniteTimeSpan); - - public static Task<TransmissionResult> ReceiveFromAsync(this Connection instance, - EndPoint remoteEndPoint, Memory<byte> inputBuffer, SocketFlags flags) - => instance.ReceiveFromAsync(remoteEndPoint, inputBuffer, flags, Timeout.InfiniteTimeSpan); - - public static ValueTask<int> SendAsync(this Connection instance, - EndPoint remoteEndPoint, Memory<byte> outputBuffer, SocketFlags flags) - => instance.SendAsync(outputBuffer, flags, Timeout.InfiniteTimeSpan); - - public static ValueTask<int> SendToAsync(this Connection instance, - EndPoint remoteEndPoint, Memory<byte> outputBuffer, SocketFlags flags) - => instance.SendToAsync(remoteEndPoint, outputBuffer, flags, Timeout.InfiniteTimeSpan); - - /// <summary> - /// Attempts to synchronously bind the underlying socket to the given local endpoint. Blocks. - /// If the timeout is exceeded the binding attempt is aborted and the method returns false. - /// </summary> - /// <param name="localEndPoint">The local endpoint to bind to.</param> - /// <param name="timeout">The timeout within which to attempt the binding.</param> - /// <returns>Whether the binding was successful or not.</returns> - public static bool TryBind(this Connection instance, - EndPoint localEndPoint, TimeSpan timeout) - => instance.TryBindAsync(localEndPoint, timeout).Result; - - public static bool TryBind(this Connection instance, - EndPoint localEndPoint) - => instance.TryBindAsync(localEndPoint, Timeout.InfiniteTimeSpan).Result; - - public static Task<bool> TryBindAsync(this Connection instance, - EndPoint localEndPoint) - => instance.TryBindAsync(localEndPoint, Timeout.InfiniteTimeSpan); - - public static bool TryConnect(this Connection instance, - EndPoint remoteEndPoint) - => instance.TryConnectAsync(remoteEndPoint, Timeout.InfiniteTimeSpan).Result; - - public static bool TryConnect(this Connection instance, - EndPoint remoteEndPoint, TimeSpan timeout) - => instance.TryConnectAsync(remoteEndPoint, timeout).Result; - - public static Task<bool> TryConnectAsync(this Connection instance, - EndPoint remoteEndPoint) - => instance.TryConnectAsync(remoteEndPoint, Timeout.InfiniteTimeSpan); - - public static bool TryDisconnect(this Connection instance) - => instance.TryDisconnectAsync(Timeout.InfiniteTimeSpan).Result; - - public static bool TryDisconnect(this Connection instance, - TimeSpan timeout) - => instance.TryDisconnectAsync(timeout).Result; - - public static Task<bool> TryDisconnectAsync(this Connection instance) - => instance.TryDisconnectAsync(Timeout.InfiniteTimeSpan); - } -} -\ No newline at end of file diff --git a/NetSharp/NetSharp/Logging/Logger.cs b/NetSharp/NetSharp/Logging/Logger.cs @@ -1,194 +0,0 @@ -using System; -using System.IO; -using System.Text; -using System.Threading.Tasks; - -namespace NetSharp.Logging -{ - /// <summary> - /// Specifies the severity level of a log message. - /// </summary> - public enum LogLevel - { - /// <summary> - /// The logged message contains some information. Lowest severity. - /// </summary> - Info, - - /// <summary> - /// The logged message contains a warning. Higher severity. - /// </summary> - Warn, - - /// <summary> - /// The logged message contains details about an error. Higher severity. - /// </summary> - Error, - - /// <summary> - /// The logged message contains details about an exception. Highest severity. - /// </summary> - Exception - } - - /// <summary> - /// A simple logger capable of writing text to a stream. - /// </summary> - public readonly struct Logger : IDisposable - { - /// <summary> - /// The stream to which messages will be logged. - /// </summary> - private readonly Stream loggingStream; - - /// <summary> - /// The minimum severity that log messages need to be logged to the underlying stream. - /// </summary> - private readonly LogLevel minimumSeverity; - - /// <summary> - /// The text writer we will use to log messages to the underlying stream. - /// </summary> - private readonly StreamWriter writer; - - /// <summary> - /// Initialises a new instance of the <see cref="Logger"/> struct. - /// </summary> - /// <param name="outputStream">The stream that the logger instance should log messages to.</param> - /// <param name="minimumLogSeverity">The minimum log level that will be logged to the output stream.</param> - public Logger(Stream outputStream, LogLevel minimumLogSeverity = LogLevel.Info) - { - loggingStream = outputStream; - writer = new StreamWriter(loggingStream, Encoding.Default) { AutoFlush = true }; - - minimumSeverity = minimumLogSeverity; - } - - /// <inheritdoc /> - public void Dispose() - { - loggingStream.Dispose(); - writer.Dispose(); - } - - /// <summary> - /// Logs a message to the underlying stream, along with the given exception and at the given severity. - /// </summary> - /// <param name="message">The message that should be logged.</param> - /// <param name="exception">The exception that occurred (if any).</param> - /// <param name="severity">The severity of the message that is being logged.</param> - public void Log(string message, Exception? exception, LogLevel severity) - { - if (severity < minimumSeverity) return; - - string severityTag = severity switch - { - LogLevel.Info => "Info ", - LogLevel.Warn => "Warn ", - LogLevel.Error => "Error", - LogLevel.Exception => "Excep", - _ => "Info " - }; - - writer.WriteLine($"[{severityTag}] {message} {exception}"); - } - - /// <summary> - /// Logs a message asynchronously to the underlying stream, along with the given exception and at the given severity. - /// </summary> - /// <param name="message">The message that should be logged.</param> - /// <param name="exception">The exception that occurred (if any).</param> - /// <param name="severity">The severity of the message that is being logged.</param> - public async Task LogAsync(string message, Exception? exception, LogLevel severity) - { - if (loggingStream.Equals(Stream.Null)) - { - // ignore log request if the underlying stream is null - return; - } - - if (!exception?.Equals(default) ?? false) - { - severity = LogLevel.Exception; - } - - if (severity >= minimumSeverity) - { - string severityTag = severity switch - { - LogLevel.Info => "Info ", - LogLevel.Warn => "Warn ", - LogLevel.Error => "Error", - LogLevel.Exception => "Excep", - _ => "Info " - }; - - await writer.WriteLineAsync($"[{severityTag}] {message} {exception}"); - } - } - - /// <summary> - /// Logs an error to the underlying stream, with severity <see cref="LogLevel.Info"/>. - /// </summary> - /// <param name="message">The error that should be logged.</param> - public void LogError(string message) => Log(message, null, LogLevel.Error); - - /// <summary> - /// Logs an error to the underlying stream asynchronously, with severity <see cref="LogLevel.Error"/>. - /// </summary> - /// <param name="message">The error that should be logged.</param> - public async Task LogErrorAsync(string message) => await LogAsync(message, null, LogLevel.Error); - - /// <summary> - /// Logs an exception to the underlying stream, with severity <see cref="LogLevel.Exception"/>. - /// </summary> - /// <param name="exception">The exception that should be logged.</param> - public void LogException(Exception exception) => Log("", exception, LogLevel.Exception); - - /// <summary> - /// Logs an exception to the underlying stream, along with a short debug message, with severity - /// <see cref="LogLevel.Exception"/>. - /// </summary> - /// <param name="message">The debug message that should be logged with the exception.</param> - /// <param name="exception">The exception that should be logged.</param> - public void LogException(string message, Exception exception) => Log(message, exception, LogLevel.Exception); - - /// <summary> - /// Logs an exception to the underlying stream asynchronously, with severity <see cref="LogLevel.Exception"/>. - /// </summary> - /// <param name="exception">The exception that should be logged.</param> - public async Task LogExceptionAsync(Exception exception) => await LogAsync("", exception, LogLevel.Exception); - - /// <summary> - /// Logs an exception to the underlying stream asynchronously, along with a short debug message, with severity - /// <see cref="LogLevel.Exception"/>. - /// </summary> - /// <param name="message">The debug message that should be logged with the exception.</param> - /// <param name="exception">The exception that should be logged.</param> - public async Task LogExceptionAsync(string message, Exception exception) => await LogAsync(message, exception, LogLevel.Exception); - - /// <summary> - /// Logs a message to the underlying stream, with severity <see cref="LogLevel.Info"/>. - /// </summary> - /// <param name="message">The message that should be logged.</param> - public void LogMessage(string message) => Log(message, null, LogLevel.Info); - - /// <summary> - /// Logs a message to the underlying stream asynchronously, with severity <see cref="LogLevel.Info"/>. - /// </summary> - /// <param name="message">The message that should be logged.</param> - public async Task LogMessageAsync(string message) => await LogAsync(message, null, LogLevel.Info); - - /// <summary> - /// Logs a warning to the underlying stream, with severity <see cref="LogLevel.Info"/>. - /// </summary> - /// <param name="message">The warning that should be logged.</param> - public void LogWarning(string message) => Log(message, null, LogLevel.Warn); - - /// <summary> - /// Logs a warning to the underlying stream asynchronously, with severity <see cref="LogLevel.Warn"/>. - /// </summary> - /// <param name="message">The warning that should be logged.</param> - public async Task LogWarningAsync(string message) => await LogAsync(message, null, LogLevel.Warn); - } -} -\ No newline at end of file diff --git a/NetSharp/NetSharp/NetSharp.csproj b/NetSharp/NetSharp/NetSharp.csproj @@ -17,12 +17,14 @@ </PropertyGroup> <ItemGroup> - <PackageReference Include="Microsoft.CSharp" Version="4.7.0" /> - <PackageReference Include="Microsoft.Extensions.ObjectPool" Version="3.1.2" /> - <PackageReference Include="System.Threading.Channels" Version="4.7.0" /> + <Compile Remove="Interfaces\**" /> + <EmbeddedResource Remove="Interfaces\**" /> + <None Remove="Interfaces\**" /> </ItemGroup> <ItemGroup> - <Folder Include="Interfaces\" /> + <PackageReference Include="Microsoft.CSharp" Version="4.7.0" /> + <PackageReference Include="Microsoft.Extensions.ObjectPool" Version="3.1.2" /> + <PackageReference Include="System.Threading.Channels" Version="4.7.0" /> </ItemGroup> </Project> \ No newline at end of file diff --git a/NetSharp/NetSharp/NetSharp.xml b/NetSharp/NetSharp/NetSharp.xml @@ -4,242 +4,198 @@ <name>NetSharp</name> </assembly> <members> - <member name="T:NetSharp.Connection"> + <member name="T:NetSharp.Deprecated.Builtin.ConnectPacket"> <summary> - Encapsulates a connection capable of receiving packets and responding to them with registered packet handlers. - </summary> - <summary> - Implements low-level network access on top of which the rest of the connection is built upon. + A simple connection request packet for the UDP protocol. </summary> </member> - <member name="F:NetSharp.Connection.incomingPacketPipeline"> - <summary> - Pipeline to convert incoming byte buffers to <see cref="T:NetSharp.Packets.NetworkPacket"/> instances. - </summary> + <member name="M:NetSharp.Deprecated.Builtin.ConnectPacket.AfterDeserialisation"> + <inheritdoc /> </member> - <member name="F:NetSharp.Connection.loggerLockObject"> - <summary> - Lock synchronisation object for the <see cref="F:NetSharp.Connection.logger"/> variable. - </summary> + <member name="M:NetSharp.Deprecated.Builtin.ConnectPacket.BeforeSerialisation"> + <inheritdoc /> </member> - <member name="F:NetSharp.Connection.outgoingPacketPipeline"> - <summary> - Pipeline to convert outgoing <see cref="T:NetSharp.Packets.NetworkPacket"/> instances to a byte buffer for sending. - </summary> + <member name="M:NetSharp.Deprecated.Builtin.ConnectPacket.Deserialise(System.ReadOnlyMemory{System.Byte})"> + <inheritdoc /> </member> - <member name="F:NetSharp.Connection.ServerShutdownToken"> - <summary> - Cancellation token which allows observing the shutdown of the server. It is set when <see cref="M:NetSharp.Connection.ShutdownServer"/> is called. - </summary> + <member name="M:NetSharp.Deprecated.Builtin.ConnectPacket.Serialise"> + <inheritdoc /> </member> - <member name="F:NetSharp.Connection.logger"> + <member name="T:NetSharp.Deprecated.Builtin.ConnectResponsePacket"> <summary> - A logger object allowing for writing debug messages to an output stream. + A response packet for the <see cref="T:NetSharp.Deprecated.Builtin.ConnectPacket"/>. </summary> </member> - <member name="M:NetSharp.Connection.Finalize"> - <summary> - Destroys a <see cref="T:NetSharp.Connection"/> class instance, freeing all managed resources. - </summary> + <member name="P:NetSharp.Deprecated.Builtin.ConnectResponsePacket.RequestPacket"> + <inheritdoc /> </member> - <member name="M:NetSharp.Connection.RunServerAsync"> - <summary> - Makes the connection listen for incoming request packets, and handle them according to registered packet handler delegates. - This work can be cancelled by calling <see cref="M:NetSharp.Connection.ShutdownServer"/>. - </summary> - <returns>The task representing the connection work.</returns> + <member name="M:NetSharp.Deprecated.Builtin.ConnectResponsePacket.AfterDeserialisation"> + <inheritdoc /> </member> - <member name="M:NetSharp.Connection.ShutdownServer"> - <summary> - Shuts down the connection, and releases managed and unmanaged resources. - </summary> + <member name="M:NetSharp.Deprecated.Builtin.ConnectResponsePacket.BeforeSerialisation"> + <inheritdoc /> </member> - <member name="F:NetSharp.Connection.AnyRemoteEndPoint"> - <summary> - Represents any remote endpoint for datagram operations. - </summary> + <member name="M:NetSharp.Deprecated.Builtin.ConnectResponsePacket.Deserialise(System.ReadOnlyMemory{System.Byte})"> + <inheritdoc /> </member> - <member name="M:NetSharp.Connection.Dispose(System.Boolean)"> - <summary> - Disposes of the managed and unmanaged resources held by this instance. - </summary> - <param name="disposing">Whether this method is called by <see cref="M:NetSharp.Connection.Dispose"/> or by the finaliser.</param> + <member name="M:NetSharp.Deprecated.Builtin.ConnectResponsePacket.Serialise"> + <inheritdoc /> </member> - <member name="M:NetSharp.Connection.DoAcceptAsync(System.Net.Sockets.Socket,System.Threading.CancellationToken)"> + <member name="T:NetSharp.Deprecated.Builtin.DataPacket"> <summary> - Provides an awaitable wrapper around an asynchronous socket accept operation. + A simple data transfer packet, that allows for the transmission of an arbitrary number of frames. </summary> - <param name="serverSocket">The socket which should be used to accept an incoming connection attempt.</param> - <param name="cancellationToken">The cancellation token to observe for the operation.</param> - <returns>The accepted socket.</returns> </member> - <member name="M:NetSharp.Connection.DoConnectAsync(System.Net.Sockets.Socket,System.Net.EndPoint,System.Threading.CancellationToken)"> + <member name="F:NetSharp.Deprecated.Builtin.DataPacket.RequestBuffer"> <summary> - Provides an awaitable wrapper around an asynchronous socket connect operation. + The data that should be transferred across the network. </summary> - <param name="socket">The socket which should asynchronously connect to the remote endpoint.</param> - <param name="remoteEndPoint">The remote endpoint to which the socket should connect.</param> - <param name="cancellationToken">The cancellation token to observe for the operation.</param> </member> - <member name="M:NetSharp.Connection.DoDisconnectAsync(System.Net.Sockets.Socket,System.Threading.CancellationToken)"> + <member name="M:NetSharp.Deprecated.Builtin.DataPacket.#ctor"> <summary> - Provides an awaitable wrapper around an asynchronous socket disconnect operation. + Initialises a new instance of the <see cref="T:NetSharp.Deprecated.Builtin.DataPacket"/> class. </summary> - <param name="connectedSocket">The socket which should asynchronously disconnect from its remote endpoint.</param> - <param name="cancellationToken">The cancellation token to observe for the operation.</param> </member> - <member name="M:NetSharp.Connection.DoReceiveFromAsync(System.Net.Sockets.Socket,System.Net.EndPoint,System.Net.Sockets.SocketFlags,System.Memory{System.Byte},System.Threading.CancellationToken)"> + <member name="M:NetSharp.Deprecated.Builtin.DataPacket.#ctor(System.Memory{System.Byte})"> <summary> - Provides an awaitable wrapper around an asynchronous socket receive operation. + Initialises a new instance of the <see cref="T:NetSharp.Deprecated.Builtin.DataPacket"/> class. </summary> - <param name="listenerSocket">The socket which should receive data from the remote endpoint.</param> - <param name="remoteEndPoint">The remove endpoint from which data should be received.</param> - <param name="socketFlags">The socket flags associated with the receive operation.</param> - <param name="inputBuffer">The memory buffer into which received data will be stored.</param> - <param name="cancellationToken">The cancellation token to observe for the operation.</param> - <returns>The result of the receive operation from the remote endpoint.</returns> + <param name="buffer">The data that this request packet should contain.</param> </member> - <member name="M:NetSharp.Connection.DoSendToAsync(System.Net.Sockets.Socket,System.Net.EndPoint,System.Net.Sockets.SocketFlags,System.Memory{System.Byte},System.Threading.CancellationToken)"> - <summary> - Provides an awaitable wrapper around an asynchronous socket send operation. - </summary> - <param name="transmitterSocket">The socket which should send the data to the remote endpoint.</param> - <param name="remoteEndPoint">The remote endpoint to which data should be written.</param> - <param name="socketFlags">The socket flags associated with the send operation.</param> - <param name="outputBuffer">The data buffer which should be sent.</param> - <param name="cancellationToken">The cancellation token to observe for the operation.</param> - <returns>The result of the send operation to the remote endpoint.</returns> + <member name="M:NetSharp.Deprecated.Builtin.DataPacket.AfterDeserialisation"> + <inheritdoc /> </member> - <member name="F:NetSharp.Connection.MaximumConnectionBacklog"> - <summary> - The maximum number of stream connection that will be accepted. - </summary> - TODO change this to a configurable builder option + <member name="M:NetSharp.Deprecated.Builtin.DataPacket.BeforeSerialisation"> + <inheritdoc /> </member> - <member name="F:NetSharp.Connection.MaximumPacketBacklog"> - <summary> - The maximum number of packets that will be stored before older packets start to be dropped. - </summary> - TODO change this to a configurable builder option + <member name="M:NetSharp.Deprecated.Builtin.DataPacket.Deserialise(System.ReadOnlyMemory{System.Byte})"> + <inheritdoc /> </member> - <member name="M:NetSharp.Connection.Dispose"> + <member name="M:NetSharp.Deprecated.Builtin.DataPacket.Serialise"> <inheritdoc /> </member> - <member name="M:NetSharp.Connection.SetLoggingStream(System.IO.Stream,NetSharp.Logging.LogLevel)"> + <member name="T:NetSharp.Deprecated.Builtin.DataResponsePacket"> <summary> - Configures the logger to log messages to the given stream (or to <see cref="F:System.IO.Stream.Null"/> if <c>null</c>) and - to only log messages that are of severity <paramref name="minimumLoggedSeverity"/> or higher. + A response packet for the <see cref="T:NetSharp.Deprecated.Builtin.DataPacket"/>. </summary> - <param name="loggingStream">The stream to which messages will be logged.</param> - <param name="minimumLoggedSeverity">The minimum severity a message must be to be logged.</param> </member> - <member name="M:NetSharp.Connection.TryBindAsync(System.Net.EndPoint,System.TimeSpan)"> + <member name="F:NetSharp.Deprecated.Builtin.DataResponsePacket.ResponseBuffer"> <summary> - Attempts to asynchronously bind the underlying socket to the given local endpoint. Does not block. - If the timeout is exceeded the binding attempt is aborted and the method returns false. + The data that should be transferred across the network. </summary> - <param name="localEndPoint">The local endpoint to bind to.</param> - <param name="timeout">The timeout within which to attempt the binding.</param> - <returns>Whether the binding was successful or not.</returns> </member> - <member name="T:NetSharp.ConnectionBuilder"> + <member name="M:NetSharp.Deprecated.Builtin.DataResponsePacket.#ctor"> <summary> - Allows for configuring and subsequently building a <see cref="T:NetSharp.Connection"/> instance. + Initialises a new instance of the <see cref="T:NetSharp.Deprecated.Builtin.DataResponsePacket"/> class. </summary> </member> - <member name="P:NetSharp.ConnectionBuilder.IncomingPacketPipelineStageCount"> + <member name="M:NetSharp.Deprecated.Builtin.DataResponsePacket.#ctor(System.Memory{System.Byte})"> <summary> - The number of stages in the currently configured incoming packet pipeline. + Initialises a new instance of the <see cref="T:NetSharp.Deprecated.Builtin.DataResponsePacket"/> class. </summary> + <param name="buffer">The data that this response packet should contain.</param> </member> - <member name="P:NetSharp.ConnectionBuilder.OutgoingPacketPipelineStageCount"> - <summary> - The number of stages in the currently configured outgoing packet pipeline. - </summary> + <member name="P:NetSharp.Deprecated.Builtin.DataResponsePacket.RequestPacket"> + <inheritdoc /> </member> - <member name="M:NetSharp.ConnectionBuilder.Build"> - <summary> - Returns a new <see cref="T:NetSharp.Connection"/> instance with the current configuration. - </summary> - <returns>The configured <see cref="T:NetSharp.Connection"/> instance.</returns> + <member name="M:NetSharp.Deprecated.Builtin.DataResponsePacket.AfterDeserialisation"> + <inheritdoc /> </member> - <member name="M:NetSharp.ConnectionBuilder.WithIncomingPipelineStage(System.Func{System.Memory{System.Byte},System.Memory{System.Byte}}@,System.Int32)"> - <summary> - Adds an extra pipeline stage to the currently configured incoming packet pipeline, at the given index. - </summary> - <param name="transform"> - The transformation that should be applied when a packet passes through the pipeline. - </param> - <param name="index">The position in the pipeline at which to place the transform.</param> - <returns>The builder instance for further configuration.</returns> + <member name="M:NetSharp.Deprecated.Builtin.DataResponsePacket.BeforeSerialisation"> + <inheritdoc /> </member> - <member name="M:NetSharp.ConnectionBuilder.WithLogging(NetSharp.ConnectionBuilder.LoggingSettings)"> - <summary> - Sets the logging settings for the currently configured connection. - </summary> - <param name="settings">The logging settings to use.</param> - <returns>The builder instance for further configuration.</returns> + <member name="M:NetSharp.Deprecated.Builtin.DataResponsePacket.Deserialise(System.ReadOnlyMemory{System.Byte})"> + <inheritdoc /> </member> - <member name="M:NetSharp.ConnectionBuilder.WithOutgoingPipelineStage(System.Func{System.Memory{System.Byte},System.Memory{System.Byte}}@,System.Int32)"> - <summary> - Adds an extra pipeline stage to the currently configured outgoing packet pipeline, at the given index. - </summary> - <param name="transform"> - The transformation that should be applied when a packet passes through the pipeline. - </param> - <param name="index">The position in the pipeline at which to place the transform.</param> - <returns>The builder instance for further configuration.</returns> + <member name="M:NetSharp.Deprecated.Builtin.DataResponsePacket.Serialise"> + <inheritdoc /> </member> - <member name="M:NetSharp.ConnectionBuilder.WithPooling(NetSharp.ConnectionBuilder.PoolingSettings)"> + <member name="T:NetSharp.Deprecated.Builtin.DisconnectPacket"> <summary> - Sets the pooling settings for the currently configured connection. + A simple disconnect packet for the UDP protocol. </summary> - <param name="settings">The pooling settings to use.</param> - <returns>The builder instance for further configuration.</returns> </member> - <member name="T:NetSharp.ConnectionBuilder.LoggingSettings"> - <summary> - Holds settings for configuring a connection's logging. - </summary> + <member name="M:NetSharp.Deprecated.Builtin.DisconnectPacket.AfterDeserialisation"> + <inheritdoc /> </member> - <member name="F:NetSharp.ConnectionBuilder.LoggingSettings.LoggingStream"> - <summary> - The stream to which messages will be logged. - </summary> + <member name="M:NetSharp.Deprecated.Builtin.DisconnectPacket.BeforeSerialisation"> + <inheritdoc /> + </member> + <member name="M:NetSharp.Deprecated.Builtin.DisconnectPacket.Deserialise(System.ReadOnlyMemory{System.Byte})"> + <inheritdoc /> </member> - <member name="F:NetSharp.ConnectionBuilder.LoggingSettings.MinimumLevel"> + <member name="M:NetSharp.Deprecated.Builtin.DisconnectPacket.Serialise"> + <inheritdoc /> + </member> + <member name="T:NetSharp.Deprecated.Builtin.PingPacket"> <summary> - The minimum severity that a log message must have to be recorded. + A simple ping request packet for heartbeat monitoring and RTT measurement. </summary> </member> - <member name="M:NetSharp.ConnectionBuilder.LoggingSettings.#ctor(System.IO.Stream,NetSharp.Logging.LogLevel)"> + <member name="M:NetSharp.Deprecated.Builtin.PingPacket.AfterDeserialisation"> + <inheritdoc /> + </member> + <member name="M:NetSharp.Deprecated.Builtin.PingPacket.BeforeSerialisation"> + <inheritdoc /> + </member> + <member name="M:NetSharp.Deprecated.Builtin.PingPacket.Deserialise(System.ReadOnlyMemory{System.Byte})"> + <inheritdoc /> + </member> + <member name="M:NetSharp.Deprecated.Builtin.PingPacket.Serialise"> + <inheritdoc /> + </member> + <member name="T:NetSharp.Deprecated.Builtin.PingResponsePacket"> <summary> - Initialises a new instance of the <see cref="F:NetSharp.ConnectionBuilder.LoggingSettings.LoggingStream"/> struct. + A response packet for the <see cref="T:NetSharp.Deprecated.Builtin.PingPacket"/>. </summary> - <param name="stream">The stream to which messages will be logged..</param> - <param name="minimumLevel">The minimum severity that a log message must have to be recorded.</param> </member> - <member name="T:NetSharp.ConnectionBuilder.PoolingSettings"> + <member name="P:NetSharp.Deprecated.Builtin.PingResponsePacket.RequestPacket"> + <inheritdoc /> + </member> + <member name="M:NetSharp.Deprecated.Builtin.PingResponsePacket.AfterDeserialisation"> + <inheritdoc /> + </member> + <member name="M:NetSharp.Deprecated.Builtin.PingResponsePacket.BeforeSerialisation"> + <inheritdoc /> + </member> + <member name="M:NetSharp.Deprecated.Builtin.PingResponsePacket.Deserialise(System.ReadOnlyMemory{System.Byte})"> + <inheritdoc /> + </member> + <member name="M:NetSharp.Deprecated.Builtin.PingResponsePacket.Serialise"> + <inheritdoc /> + </member> + <member name="T:NetSharp.Deprecated.Builtin.SimpleDataPacket"> <summary> - Holds settings for configuring a connection's buffer pooling. + A simple one-time-use data transfer packet, that allows for the transmission of an arbitrary number of frames. </summary> </member> - <member name="F:NetSharp.ConnectionBuilder.PoolingSettings.ObjectPoolSize"> + <member name="F:NetSharp.Deprecated.Builtin.SimpleDataPacket.RequestBuffer"> <summary> - The number of objects that will be held in the object pools. + The data that should be transferred across the network. </summary> </member> - <member name="F:NetSharp.ConnectionBuilder.PoolingSettings.PreallocateBuffers"> + <member name="M:NetSharp.Deprecated.Builtin.SimpleDataPacket.#ctor"> <summary> - Whether the buffers for receiving messages should be preallocated. + Initialises a new instance of the <see cref="T:NetSharp.Deprecated.Builtin.SimpleDataPacket"/> class. </summary> </member> - <member name="M:NetSharp.ConnectionBuilder.PoolingSettings.#ctor(System.Int32,System.Boolean)"> + <member name="M:NetSharp.Deprecated.Builtin.SimpleDataPacket.#ctor(System.Memory{System.Byte})"> <summary> - Initialises a new instance of the <see cref="T:NetSharp.ConnectionBuilder.PoolingSettings"/> struct. + Initialises a new instance of the <see cref="T:NetSharp.Deprecated.Builtin.SimpleDataPacket"/> class. </summary> - <param name="poolSize">The number of objects that will be held in the object pools.</param> - <param name="preallocateBuffers">Whether the buffers for receiving messages should be preallocated.</param> + <param name="buffer">The data that this request packet should contain.</param> + </member> + <member name="M:NetSharp.Deprecated.Builtin.SimpleDataPacket.AfterDeserialisation"> + <inheritdoc /> + </member> + <member name="M:NetSharp.Deprecated.Builtin.SimpleDataPacket.BeforeSerialisation"> + <inheritdoc /> + </member> + <member name="M:NetSharp.Deprecated.Builtin.SimpleDataPacket.Deserialise(System.ReadOnlyMemory{System.Byte})"> + <inheritdoc /> + </member> + <member name="M:NetSharp.Deprecated.Builtin.SimpleDataPacket.Serialise"> + <inheritdoc /> </member> <member name="T:NetSharp.Deprecated.Client"> <summary> @@ -510,1565 +466,1512 @@ <param name="remotePort">The remote port to connect over.</param> <returns>Whether the connection was successful or not.</returns> </member> - <member name="T:NetSharp.Deprecated.DefaultSocketOptions"> + <member name="T:NetSharp.Deprecated.Connection"> <summary> - Allows for manipulation of socket options. + Encapsulates a connection capable of receiving packets and responding to them with registered packet handlers. + </summary> + <summary> + Implements low-level network access on top of which the rest of the connection is built upon. </summary> </member> - <member name="M:NetSharp.Deprecated.DefaultSocketOptions.#ctor(System.Net.Sockets.Socket@)"> - <inheritdoc /> - </member> - <member name="P:NetSharp.Deprecated.DefaultSocketOptions.HopLimit"> - <inheritdoc /> - <exception cref="T:System.NotSupportedException"> - This property is not supported when using the default socket option manager. - </exception> - </member> - <member name="P:NetSharp.Deprecated.DefaultSocketOptions.IsRoutingEnabled"> - <inheritdoc /> - <exception cref="T:System.NotSupportedException"> - This property is not supported when using the default socket option manager. - </exception> - </member> - <member name="P:NetSharp.Deprecated.DefaultSocketOptions.UseLoopback"> - <inheritdoc /> - <exception cref="T:System.NotSupportedException"> - This property is not supported when using the default socket option manager. - </exception> + <member name="F:NetSharp.Deprecated.Connection.incomingPacketPipeline"> + <summary> + Pipeline to convert incoming byte buffers to <see cref="T:NetSharp.Deprecated.NetworkPacket"/> instances. + </summary> </member> - <member name="T:NetSharp.Deprecated.IClient"> + <member name="F:NetSharp.Deprecated.Connection.loggerLockObject"> <summary> - Describes a client capable of asynchronous communication with an <see cref="T:NetSharp.Deprecated.IServer"/> connection. + Lock synchronisation object for the <see cref="F:NetSharp.Deprecated.Connection.logger"/> variable. </summary> </member> - <member name="E:NetSharp.Deprecated.IClient.Connected"> + <member name="F:NetSharp.Deprecated.Connection.outgoingPacketPipeline"> <summary> - Signifies that a connection with the remote endpoint has been made. + Pipeline to convert outgoing <see cref="T:NetSharp.Deprecated.NetworkPacket"/> instances to a byte buffer for sending. </summary> </member> - <member name="E:NetSharp.Deprecated.IClient.Disconnected"> + <member name="F:NetSharp.Deprecated.Connection.ServerShutdownToken"> <summary> - Signifies that the connection with the remote endpoint was severed. + Cancellation token which allows observing the shutdown of the server. It is set when <see cref="M:NetSharp.Deprecated.Connection.ShutdownServer"/> is called. </summary> </member> - <member name="M:NetSharp.Deprecated.IClient.SendBytesAsync(System.Byte[],System.TimeSpan)"> + <member name="F:NetSharp.Deprecated.Connection.logger"> <summary> - Sends the given byte buffer to the connected remote endpoint asynchronously. Does not block, and observes - a timeout of the given length. - timeout. + A logger object allowing for writing debug messages to an output stream. </summary> - <param name="buffer">The bytes that should be sent to the connected remote endpoint.</param> - <param name="timeout">The timeout after which to cancel the transmission attempt.</param> - <returns>Whether the transmission attempt was successful.</returns> </member> - <member name="M:NetSharp.Deprecated.IClient.SendBytesWithResponseAsync(System.Byte[],System.TimeSpan)"> + <member name="M:NetSharp.Deprecated.Connection.Finalize"> <summary> - Sends the given byte buffer to the connected remote endpoint and waits for the response asynchronously. - Does not block, and observes a timeout of the given length. - timeout. + Destroys a <see cref="T:NetSharp.Deprecated.Connection"/> class instance, freeing all managed resources. </summary> - <param name="buffer">The bytes that should be sent to the connected remote endpoint.</param> - <param name="timeout"> - The timeout after which to cancel the transmission attempt. This timeout is reused by both the 'send' and - 'receive' parts of the transmission attempt, such that the maximum timeout is equal to 2 times the given - value. - </param> - <returns>The byte buffer received as a response to the sent buffer.</returns> </member> - <member name="M:NetSharp.Deprecated.IClient.SendComplexAsync``2(``0,System.TimeSpan)"> + <member name="M:NetSharp.Deprecated.Connection.RunServerAsync"> <summary> - Sends the given request and listens for a response of the given type asynchronously. Does not block. - Cancels the operation if the given timeout is exceeded + Makes the connection listen for incoming request packets, and handle them according to registered packet handler delegates. + This work can be cancelled by calling <see cref="M:NetSharp.Deprecated.Connection.ShutdownServer"/>. </summary> - <typeparam name="Req">The type of request packet to send.</typeparam> - <typeparam name="Rep">The type of response packet to receive.</typeparam> - <param name="request">The request packet to send.</param> - <param name="timeout">The timeout for which to wait for the operation to complete.</param> - <returns>The received instance.</returns> + <returns>The task representing the connection work.</returns> </member> - <member name="M:NetSharp.Deprecated.IClient.SendSimpleAsync``1(``0,System.TimeSpan)"> + <member name="M:NetSharp.Deprecated.Connection.ShutdownServer"> <summary> - Sends the given request asynchronously without listening for a response, not blocking until it is sent. - Cancels the operation if the given timeout is exceeded. + Shuts down the connection, and releases managed and unmanaged resources. </summary> - <typeparam name="Req">The type of request packet to send.</typeparam> - <param name="request">The request packet to send.</param> - <param name="timeout">The timeout for which to wait for the operation to complete.</param> - <returns>Whether the transmission attempt was successful.</returns> </member> - <member name="M:NetSharp.Deprecated.IClient.TryBindAsync(System.Net.IPAddress,System.Nullable{System.Int32},System.TimeSpan)"> + <member name="F:NetSharp.Deprecated.Connection.AnyRemoteEndPoint"> <summary> - Attempts to asynchronously bind the underlying socket to the given local address and port. Does not block. - If the timeout is exceeded the binding attempt is aborted and the method returns false. + Represents any remote endpoint for datagram operations. </summary> - <param name="localAddress">The local IP address to bind to. Null if any IP address will suffice.</param> - <param name="localPort">The local port to bind to. Null if any port will suffice.</param> - <param name="timeout">The timeout within which to attempt the binding.</param> - <returns>Whether the binding was successful or not.</returns> </member> - <member name="M:NetSharp.Deprecated.IClient.TryConnectAsync(System.Net.IPAddress,System.Int32,System.TimeSpan)"> + <member name="M:NetSharp.Deprecated.Connection.Dispose(System.Boolean)"> <summary> - Attempts to connect asynchronously to the remote <see cref="T:NetSharp.Deprecated.Server"/> at the given <see cref="T:System.Net.IPAddress"/> - and over the given port. If the timeout is exceeded the connection attempt is aborted and the method returns false. - </summary> - <param name="remoteAddress">The remote IP address to connect to.</param> - <param name="remotePort">The remote port to connect over.</param> - <param name="timeout">The timeout within which to attempt the connection.</param> - <returns>Whether the connection was successful or not.</returns> - </member> - <member name="T:NetSharp.Deprecated.INetworkSerialisable"> - <summary> - Describes an object that can be serialised to be sent across the network. - </summary> - </member> - <member name="M:NetSharp.Deprecated.INetworkSerialisable.Deserialise(System.ReadOnlyMemory{System.Byte})"> - <summary> - Deserialises the object instance from a byte array. - </summary> - <param name="serialisedObject">The memory containing the serialised object instance.</param> - </member> - <member name="M:NetSharp.Deprecated.INetworkSerialisable.Serialise"> - <summary> - Serialises the object instance into a byte array. + Disposes of the managed and unmanaged resources held by this instance. </summary> - <returns>The memory containing the serialised object instance.</returns> + <param name="disposing">Whether this method is called by <see cref="M:NetSharp.Deprecated.Connection.Dispose"/> or by the finaliser.</param> </member> - <member name="T:NetSharp.Deprecated.IPacket"> + <member name="M:NetSharp.Deprecated.Connection.DoAcceptAsync(System.Net.Sockets.Socket,System.Threading.CancellationToken)"> <summary> - Describes the methods and properties that every packet + Provides an awaitable wrapper around an asynchronous socket accept operation. </summary> + <param name="serverSocket">The socket which should be used to accept an incoming connection attempt.</param> + <param name="cancellationToken">The cancellation token to observe for the operation.</param> + <returns>The accepted socket.</returns> </member> - <member name="M:NetSharp.Deprecated.IPacket.AfterDeserialisation"> + <member name="M:NetSharp.Deprecated.Connection.DoConnectAsync(System.Net.Sockets.Socket,System.Net.EndPoint,System.Threading.CancellationToken)"> <summary> - Allows for custom fields to be converted from their serialised format, after being received from the network. + Provides an awaitable wrapper around an asynchronous socket connect operation. </summary> + <param name="socket">The socket which should asynchronously connect to the remote endpoint.</param> + <param name="remoteEndPoint">The remote endpoint to which the socket should connect.</param> + <param name="cancellationToken">The cancellation token to observe for the operation.</param> </member> - <member name="M:NetSharp.Deprecated.IPacket.BeforeSerialisation"> + <member name="M:NetSharp.Deprecated.Connection.DoDisconnectAsync(System.Net.Sockets.Socket,System.Threading.CancellationToken)"> <summary> - Allows for custom fields to be converted into another format prior to being sent via the network. + Provides an awaitable wrapper around an asynchronous socket disconnect operation. </summary> + <param name="connectedSocket">The socket which should asynchronously disconnect from its remote endpoint.</param> + <param name="cancellationToken">The cancellation token to observe for the operation.</param> </member> - <member name="T:NetSharp.Deprecated.IPacketHandler"> + <member name="M:NetSharp.Deprecated.Connection.DoReceiveFromAsync(System.Net.Sockets.Socket,System.Net.EndPoint,System.Net.Sockets.SocketFlags,System.Memory{System.Byte},System.Threading.CancellationToken)"> <summary> - Describes a class capable of registering and deregistering packet handlers, and capable of - handling incoming packets according to the currently registered packet handlers. + Provides an awaitable wrapper around an asynchronous socket receive operation. </summary> + <param name="listenerSocket">The socket which should receive data from the remote endpoint.</param> + <param name="remoteEndPoint">The remove endpoint from which data should be received.</param> + <param name="socketFlags">The socket flags associated with the receive operation.</param> + <param name="inputBuffer">The memory buffer into which received data will be stored.</param> + <param name="cancellationToken">The cancellation token to observe for the operation.</param> + <returns>The result of the receive operation from the remote endpoint.</returns> </member> - <member name="M:NetSharp.Deprecated.IPacketHandler.TryDeregisterComplexPacketHandler``2(NetSharp.Deprecated.ComplexPacketHandler{``0,``1}@)"> + <member name="M:NetSharp.Deprecated.Connection.DoSendToAsync(System.Net.Sockets.Socket,System.Net.EndPoint,System.Net.Sockets.SocketFlags,System.Memory{System.Byte},System.Threading.CancellationToken)"> <summary> - Attempts to deregister the complex packet handler delegate for all packets of the given type. If a handler - method doesn't exist for the given packet type, <paramref name="oldHandlerDelegate"/> will be <c>default</c>. + Provides an awaitable wrapper around an asynchronous socket send operation. </summary> - <typeparam name="Req">The type of request packet for which to deregister the handler delegate.</typeparam> - <typeparam name="Rep">The type of response packet that is generated by the delegate method.</typeparam> - <param name="oldHandlerDelegate">The old handler delegate method that was previously registered.</param> - <returns>Whether the packet handler delegate was successfully deregistered.</returns> + <param name="transmitterSocket">The socket which should send the data to the remote endpoint.</param> + <param name="remoteEndPoint">The remote endpoint to which data should be written.</param> + <param name="socketFlags">The socket flags associated with the send operation.</param> + <param name="outputBuffer">The data buffer which should be sent.</param> + <param name="cancellationToken">The cancellation token to observe for the operation.</param> + <returns>The result of the send operation to the remote endpoint.</returns> </member> - <member name="M:NetSharp.Deprecated.IPacketHandler.TryDeregisterSimplePacketHandler``1(NetSharp.Deprecated.SimplePacketHandler{``0}@)"> + <member name="F:NetSharp.Deprecated.Connection.MaximumConnectionBacklog"> <summary> - Attempts to deregister the simple packet handler delegate for all packets of the given type. If a handler - method doesn't exist for the given packet type, <paramref name="oldHandlerDelegate"/> will be <c>default</c>. + The maximum number of stream connection that will be accepted. </summary> - <typeparam name="Req">The type of request packet for which to deregister the handler delegate.</typeparam> - <param name="oldHandlerDelegate">The old handler delegate method that was previously registered.</param> - <returns>Whether the packet handler delegate was successfully deregistered.</returns> + TODO change this to a configurable builder option </member> - <member name="M:NetSharp.Deprecated.IPacketHandler.TryRegisterComplexPacketHandler``2(NetSharp.Deprecated.ComplexPacketHandler{``0,``1})"> + <member name="F:NetSharp.Deprecated.Connection.MaximumPacketBacklog"> <summary> - Attempts to register a complex packet handler delegate for all packets of the given type. If a handler - method already exists for the given packet type, it will be updated and replaced with the given one. + The maximum number of packets that will be stored before older packets start to be dropped. </summary> - <typeparam name="Req">The type of request packet for which to register the handler delegate.</typeparam> - <typeparam name="Rep">The type of response packet that is generated by the delegate method.</typeparam> - <param name="handlerDelegate">The delegate method to register as the complex packet handler.</param> - <returns>Whether the packet handler delegate was successfully registered.</returns> + TODO change this to a configurable builder option </member> - <member name="M:NetSharp.Deprecated.IPacketHandler.TryRegisterSimplePacketHandler``1(NetSharp.Deprecated.SimplePacketHandler{``0})"> - <summary> - Attempts to register a simple packet handler delegate for all packets of the given type. If a handler - method already exists for the given packet type, it will be updated and replaced with the given one. - </summary> - <typeparam name="Req">The type of request packet for which to register the handler delegate.</typeparam> - <param name="handlerDelegate">The delegate method to register as the simple packet handler.</param> - <returns>Whether the packet handler delegate was successfully registered.</returns> + <member name="M:NetSharp.Deprecated.Connection.Dispose"> + <inheritdoc /> </member> - <member name="T:NetSharp.Deprecated.IRequestPacket"> + <member name="M:NetSharp.Deprecated.Connection.SetLoggingStream(System.IO.Stream,NetSharp.Deprecated.LogLevel)"> <summary> - Describes a request packet. + Configures the logger to log messages to the given stream (or to <see cref="F:System.IO.Stream.Null"/> if <c>null</c>) and + to only log messages that are of severity <paramref name="minimumLoggedSeverity"/> or higher. </summary> + <param name="loggingStream">The stream to which messages will be logged.</param> + <param name="minimumLoggedSeverity">The minimum severity a message must be to be logged.</param> </member> - <member name="T:NetSharp.Deprecated.IResponsePacket`1"> + <member name="M:NetSharp.Deprecated.Connection.TryBindAsync(System.Net.EndPoint,System.TimeSpan)"> <summary> - Describes a response packet to a request packet. + Attempts to asynchronously bind the underlying socket to the given local endpoint. Does not block. + If the timeout is exceeded the binding attempt is aborted and the method returns false. </summary> - <typeparam name="TReq">The request packet that this type is a response to.</typeparam> + <param name="localEndPoint">The local endpoint to bind to.</param> + <param name="timeout">The timeout within which to attempt the binding.</param> + <returns>Whether the binding was successful or not.</returns> </member> - <member name="P:NetSharp.Deprecated.IResponsePacket`1.RequestPacket"> + <member name="T:NetSharp.Deprecated.ConnectionBuilder"> <summary> - The request packet that was handled with this response packet. + Allows for configuring and subsequently building a <see cref="T:NetSharp.Deprecated.Connection"/> instance. </summary> </member> - <member name="T:NetSharp.Deprecated.IServer"> + <member name="P:NetSharp.Deprecated.ConnectionBuilder.IncomingPacketPipelineStageCount"> <summary> - Describes a server capable of asynchronously handling multiple <see cref="T:NetSharp.Deprecated.IClient"/> connections at once. + The number of stages in the currently configured incoming packet pipeline. </summary> </member> - <member name="E:NetSharp.Deprecated.IServer.ClientConnected"> + <member name="P:NetSharp.Deprecated.ConnectionBuilder.OutgoingPacketPipelineStageCount"> <summary> - Signifies that a connection with a remote endpoint has been made. - </summary> - </member> - <member name="E:NetSharp.Deprecated.IServer.ClientDisconnected"> - <summary> - Signifies that a connection with a remote endpoint has been lost. + The number of stages in the currently configured outgoing packet pipeline. </summary> </member> - <member name="E:NetSharp.Deprecated.IServer.ServerStarted"> + <member name="M:NetSharp.Deprecated.ConnectionBuilder.Build"> <summary> - Signifies that the server was started and clients will start being accepted. + Returns a new <see cref="T:NetSharp.Deprecated.Connection"/> instance with the current configuration. </summary> + <returns>The configured <see cref="T:NetSharp.Deprecated.Connection"/> instance.</returns> </member> - <member name="E:NetSharp.Deprecated.IServer.ServerStopped"> + <member name="M:NetSharp.Deprecated.ConnectionBuilder.WithIncomingPipelineStage(System.Func{System.Memory{System.Byte},System.Memory{System.Byte}}@,System.Int32)"> <summary> - Signifies that the server was stopped and clients will stop being accepted. + Adds an extra pipeline stage to the currently configured incoming packet pipeline, at the given index. </summary> + <param name="transform"> + The transformation that should be applied when a packet passes through the pipeline. + </param> + <param name="index">The position in the pipeline at which to place the transform.</param> + <returns>The builder instance for further configuration.</returns> </member> - <member name="M:NetSharp.Deprecated.IServer.RunAsync(System.Net.EndPoint)"> + <member name="M:NetSharp.Deprecated.ConnectionBuilder.WithLogging(NetSharp.Deprecated.ConnectionBuilder.LoggingSettings)"> <summary> - Starts the server asynchronously and starts accepting client connections. Does not block. + Sets the logging settings for the currently configured connection. </summary> - <param name="localEndPoint">The local endpoint to bind to.</param> + <param name="settings">The logging settings to use.</param> + <returns>The builder instance for further configuration.</returns> </member> - <member name="M:NetSharp.Deprecated.IServer.Shutdown"> + <member name="M:NetSharp.Deprecated.ConnectionBuilder.WithOutgoingPipelineStage(System.Func{System.Memory{System.Byte},System.Memory{System.Byte}}@,System.Int32)"> <summary> - Shuts down the server. + Adds an extra pipeline stage to the currently configured outgoing packet pipeline, at the given index. </summary> + <param name="transform"> + The transformation that should be applied when a packet passes through the pipeline. + </param> + <param name="index">The position in the pipeline at which to place the transform.</param> + <returns>The builder instance for further configuration.</returns> </member> - <member name="M:NetSharp.Deprecated.SerialisedPacket.From``1(``0)"> + <member name="M:NetSharp.Deprecated.ConnectionBuilder.WithPooling(NetSharp.Deprecated.ConnectionBuilder.PoolingSettings)"> <summary> - Serialises the given serialisable packet instance and returns the <see cref="T:NetSharp.Deprecated.SerialisedPacket"/> instance - that was generated. This method invokes <see cref="M:NetSharp.Deprecated.IPacket.BeforeSerialisation"/>. + Sets the pooling settings for the currently configured connection. </summary> - <typeparam name="T">The packet type that will be serialised.</typeparam> - <param name="serialisable">The packet instance that should be serialised.</param> - <returns>The serialised instance.</returns> + <param name="settings">The pooling settings to use.</param> + <returns>The builder instance for further configuration.</returns> </member> - <member name="M:NetSharp.Deprecated.SerialisedPacket.To``1(NetSharp.Deprecated.SerialisedPacket@)"> + <member name="T:NetSharp.Deprecated.ConnectionBuilder.LoggingSettings"> <summary> - Deserialises and returns a packet instance of the given type from the <see cref="T:NetSharp.Deprecated.SerialisedPacket"/> instance - that was given. This method invokes <see cref="M:NetSharp.Deprecated.IPacket.AfterDeserialisation"/>. + Holds settings for configuring a connection's logging. </summary> - <typeparam name="T">The packet type to which the packet should be deserialised.</typeparam> - <param name="instance">The serialised packet instance that should be deserialised.</param> - <returns>The deserialised instance.</returns> </member> - <member name="T:NetSharp.Deprecated.ComplexPacketHandler`2"> + <member name="F:NetSharp.Deprecated.ConnectionBuilder.LoggingSettings.LoggingStream"> <summary> - Represents a method that receives a request packet of the given type (<typeparamref name="TReq"/>) and - handles the request, returning a response packet of the given type (<typeparamref name="TRep"/>). + The stream to which messages will be logged. </summary> - <typeparam name="TReq">The type of request packet handled by this delegate method.</typeparam> - <typeparam name="TRep">The type of response packet returned by this delegate method.</typeparam> - <param name="requestPacket">The request packet that should be handled by this delegate method.</param> - <param name="remoteEndPoint">The remote endpoint from which the request originated.</param> - <returns>The response packet to send back to the remote endpoint from which the request originated.</returns> </member> - <member name="T:NetSharp.Deprecated.SimplePacketHandler`1"> + <member name="F:NetSharp.Deprecated.ConnectionBuilder.LoggingSettings.MinimumLevel"> <summary> - Represents a method that receives a simple request packet of the given type (<typeparamref name="TReq"/>) and - handles the request, not returning any response packets. + The minimum severity that a log message must have to be recorded. </summary> - <typeparam name="TReq">The type of request packet handled by this delegate method.</typeparam> - <param name="requestPacket">The request packet that should be handled by this delegate method.</param> - <param name="remoteEndPoint">The remote endpoint from which the request originated.</param> </member> - <member name="T:NetSharp.Deprecated.Server"> + <member name="M:NetSharp.Deprecated.ConnectionBuilder.LoggingSettings.#ctor(System.IO.Stream,NetSharp.Deprecated.LogLevel)"> <summary> - Provides methods for handling connected <see cref="T:NetSharp.Deprecated.IClient"/> instances. + Initialises a new instance of the <see cref="F:NetSharp.Deprecated.ConnectionBuilder.LoggingSettings.LoggingStream"/> struct. </summary> + <param name="stream">The stream to which messages will be logged..</param> + <param name="minimumLevel">The minimum severity that a log message must have to be recorded.</param> </member> - <member name="F:NetSharp.Deprecated.Server.complexPacketHandlers"> + <member name="T:NetSharp.Deprecated.ConnectionBuilder.PoolingSettings"> <summary> - Maps a packet type id to the complex packet handler for that packet type. + Holds settings for configuring a connection's buffer pooling. </summary> </member> - <member name="F:NetSharp.Deprecated.Server.requestPacketDeserialisers"> + <member name="F:NetSharp.Deprecated.ConnectionBuilder.PoolingSettings.ObjectPoolSize"> <summary> - Maps a packet type id to the raw packet deserialiser that deserialises raw packets to - <see cref="T:NetSharp.Deprecated.IRequestPacket"/> implementors. + The number of objects that will be held in the object pools. </summary> </member> - <member name="F:NetSharp.Deprecated.Server.serverShutdownCancellationTokenSource"> + <member name="F:NetSharp.Deprecated.ConnectionBuilder.PoolingSettings.PreallocateBuffers"> <summary> - Cancellation token source to stop handling client sockets when the server should be shut down. + Whether the buffers for receiving messages should be preallocated. </summary> </member> - <member name="F:NetSharp.Deprecated.Server.simplePacketHandlers"> + <member name="M:NetSharp.Deprecated.ConnectionBuilder.PoolingSettings.#ctor(System.Int32,System.Boolean)"> <summary> - Maps a packet type id to the simple packet handler for that packet type. + Initialises a new instance of the <see cref="T:NetSharp.Deprecated.ConnectionBuilder.PoolingSettings"/> struct. </summary> + <param name="poolSize">The number of objects that will be held in the object pools.</param> + <param name="preallocateBuffers">Whether the buffers for receiving messages should be preallocated.</param> </member> - <member name="M:NetSharp.Deprecated.Server.#ctor"> + <member name="T:NetSharp.Deprecated.ConnectionBuilderExtensions"> <summary> - Initialises a new instance of the <see cref="T:NetSharp.Deprecated.Server"/> class. + Provides additional methods and functionality to the <see cref="T:NetSharp.Deprecated.ConnectionBuilder"/> class. </summary> </member> - <member name="M:NetSharp.Deprecated.Server.Finalize"> + <member name="T:NetSharp.Deprecated.ConnectionExtensions"> <summary> - Destroys an instance of the <see cref="T:NetSharp.Deprecated.Server"/> class. + Provides additional methods and functionality to the <see cref="T:NetSharp.Deprecated.Connection"/> class. </summary> </member> - <member name="T:NetSharp.Deprecated.Server.RawRequestPacketDeserialiser"> + <member name="M:NetSharp.Deprecated.ConnectionExtensions.TryBind(NetSharp.Deprecated.Connection,System.Net.EndPoint,System.TimeSpan)"> <summary> - Represents a method that receives a raw packet, and deserialises it into an <see cref="T:NetSharp.Deprecated.IRequestPacket"/> implementor. + Attempts to synchronously bind the underlying socket to the given local endpoint. Blocks. + If the timeout is exceeded the binding attempt is aborted and the method returns false. </summary> - <param name="rawPacket">The raw packet that was received from the network.</param> - <returns>The deserialised instance of the packet.</returns> + <param name="localEndPoint">The local endpoint to bind to.</param> + <param name="timeout">The timeout within which to attempt the binding.</param> + <returns>Whether the binding was successful or not.</returns> </member> - <member name="M:NetSharp.Deprecated.Server.RegisterInternalPacketHandlers"> + <member name="T:NetSharp.Deprecated.Constants"> <summary> - Registers packet handlers for every internal library packet. + Holds internal default configurations and constants. </summary> </member> - <member name="F:NetSharp.Deprecated.Server.PendingConnectionBacklog"> + <member name="F:NetSharp.Deprecated.Constants.DefaultPort"> <summary> - The maximum number of connections that are allowed in the connection backlog. + The default port over which a connection is made. </summary> </member> - <member name="F:NetSharp.Deprecated.Server.DefaultNetworkOperationTimeout"> + <member name="T:NetSharp.Deprecated.DefaultSocketOptions"> <summary> - The default timeout value for all network operations. + Allows for manipulation of socket options. </summary> </member> - <member name="F:NetSharp.Deprecated.Server.serverShutdownCancellationToken"> - <summary> - The cancellation token that will be set when the server must be shut down. - </summary> + <member name="M:NetSharp.Deprecated.DefaultSocketOptions.#ctor(System.Net.Sockets.Socket@)"> + <inheritdoc /> </member> - <member name="F:NetSharp.Deprecated.Server.socket"> - <summary> - The <see cref="T:System.Net.Sockets.Socket"/> underlying the connection. - </summary> + <member name="P:NetSharp.Deprecated.DefaultSocketOptions.HopLimit"> + <inheritdoc /> + <exception cref="T:System.NotSupportedException"> + This property is not supported when using the default socket option manager. + </exception> </member> - <member name="F:NetSharp.Deprecated.Server.socketOptions"> - <summary> - Backing field for the <see cref="P:NetSharp.Deprecated.Server.SocketOptions"/> property. - </summary> + <member name="P:NetSharp.Deprecated.DefaultSocketOptions.IsRoutingEnabled"> + <inheritdoc /> + <exception cref="T:System.NotSupportedException"> + This property is not supported when using the default socket option manager. + </exception> </member> - <member name="F:NetSharp.Deprecated.Server.runServer"> - <summary> - Whether the server should be ran. - </summary> + <member name="P:NetSharp.Deprecated.DefaultSocketOptions.UseLoopback"> + <inheritdoc /> + <exception cref="T:System.NotSupportedException"> + This property is not supported when using the default socket option manager. + </exception> </member> - <member name="M:NetSharp.Deprecated.Server.#ctor(System.Net.Sockets.SocketType,System.Net.Sockets.ProtocolType)"> + <member name="T:NetSharp.Deprecated.IClient"> <summary> - Initialises a new instance of the <see cref="T:NetSharp.Deprecated.Server"/> class. + Describes a client capable of asynchronous communication with an <see cref="T:NetSharp.Deprecated.IServer"/> connection. </summary> - <param name="socketType">The socket type for the underlying socket.</param> - <param name="protocolType">The protocol type for the underlying socket.</param> - <param name="socketManager">The <see cref="!:Utils.Socket_Options.SocketOptions"/> implementation to use.</param> </member> - <member name="M:NetSharp.Deprecated.Server.#ctor(System.Net.Sockets.SocketType,System.Net.Sockets.ProtocolType,System.TimeSpan)"> + <member name="E:NetSharp.Deprecated.IClient.Connected"> <summary> - Initialises a new instance of the <see cref="T:NetSharp.Deprecated.Server"/> class. + Signifies that a connection with the remote endpoint has been made. </summary> - <param name="socketType">The socket type for the underlying socket.</param> - <param name="protocolType">The protocol type for the underlying socket.</param> - <param name="socketManager">The <see cref="!:Utils.Socket_Options.SocketOptions"/> manager to use.</param> - <param name="networkOperationTimeout">The timeout value for send and receive operations over the network.</param> </member> - <member name="M:NetSharp.Deprecated.Server.DeserialiseRequestPacket(System.UInt32,NetSharp.Deprecated.SerialisedPacket@)"> + <member name="E:NetSharp.Deprecated.IClient.Disconnected"> <summary> - Deserialises the given <see cref="T:NetSharp.Packets.NetworkPacket"/> struct into an <see cref="T:NetSharp.Deprecated.IRequestPacket"/> implementor. + Signifies that the connection with the remote endpoint was severed. </summary> - <param name="packetType">The type id of packet that we should deserialise to.</param> - <param name="rawRequestPacket">The packet that should be deserialised.</param> - <returns>The deserialised packet instance, cast to the <see cref="T:NetSharp.Deprecated.IRequestPacket"/> interface.</returns> </member> - <member name="M:NetSharp.Deprecated.Server.Dispose(System.Boolean)"> + <member name="M:NetSharp.Deprecated.IClient.SendBytesAsync(System.Byte[],System.TimeSpan)"> <summary> - Disposes of this <see cref="T:NetSharp.Deprecated.Server"/> instance. + Sends the given byte buffer to the connected remote endpoint asynchronously. Does not block, and observes + a timeout of the given length. + timeout. </summary> - <param name="disposing">Whether this instance is being disposed.</param> + <param name="buffer">The bytes that should be sent to the connected remote endpoint.</param> + <param name="timeout">The timeout after which to cancel the transmission attempt.</param> + <returns>Whether the transmission attempt was successful.</returns> </member> - <member name="M:NetSharp.Deprecated.Server.DoHandleClientAsync(System.Object)"> + <member name="M:NetSharp.Deprecated.IClient.SendBytesWithResponseAsync(System.Byte[],System.TimeSpan)"> <summary> - Provides a task that represents the handling of a client. Calls the abstract <see cref="M:NetSharp.Deprecated.Server.HandleClientAsync(NetSharp.Deprecated.Server.ClientHandlerArgs,System.Threading.CancellationToken)"/> method. + Sends the given byte buffer to the connected remote endpoint and waits for the response asynchronously. + Does not block, and observes a timeout of the given length. + timeout. </summary> - <param name="clientHandlerArgsObj">The object representing the passed <see cref="T:NetSharp.Deprecated.Server.ClientHandlerArgs"/> instance.</param> + <param name="buffer">The bytes that should be sent to the connected remote endpoint.</param> + <param name="timeout"> + The timeout after which to cancel the transmission attempt. This timeout is reused by both the 'send' and + 'receive' parts of the transmission attempt, such that the maximum timeout is equal to 2 times the given + value. + </param> + <returns>The byte buffer received as a response to the sent buffer.</returns> </member> - <member name="M:NetSharp.Deprecated.Server.HandleClientAsync(NetSharp.Deprecated.Server.ClientHandlerArgs,System.Threading.CancellationToken)"> + <member name="M:NetSharp.Deprecated.IClient.SendComplexAsync``2(``0,System.TimeSpan)"> <summary> - Handles a client asynchronously. + Sends the given request and listens for a response of the given type asynchronously. Does not block. + Cancels the operation if the given timeout is exceeded </summary> - <param name="args">The client handler arguments that should be passed to the client handler.</param> - <param name="cancellationToken">Cancellation token set when the server is shutting down.</param> + <typeparam name="Req">The type of request packet to send.</typeparam> + <typeparam name="Rep">The type of response packet to receive.</typeparam> + <param name="request">The request packet to send.</param> + <param name="timeout">The timeout for which to wait for the operation to complete.</param> + <returns>The received instance.</returns> </member> - <member name="M:NetSharp.Deprecated.Server.HandleRequestPacket(System.UInt32,NetSharp.Deprecated.IRequestPacket@,System.Net.EndPoint@)"> + <member name="M:NetSharp.Deprecated.IClient.SendSimpleAsync``1(``0,System.TimeSpan)"> <summary> - Handles the given request packet with a registered packet handler. In this case, a complex packet handler - will override any registered simple packet handlers. + Sends the given request asynchronously without listening for a response, not blocking until it is sent. + Cancels the operation if the given timeout is exceeded. </summary> - <param name="packetType">The type id of the packet that we should handle.</param> - <param name="requestPacket">The packet instance that should be handled.</param> - <param name="remoteEndPoint">The remote endpoint from which the request packet originated.</param> - <returns>The response packet that should be sent back to the remote endpoint.</returns> + <typeparam name="Req">The type of request packet to send.</typeparam> + <param name="request">The request packet to send.</param> + <param name="timeout">The timeout for which to wait for the operation to complete.</param> + <returns>Whether the transmission attempt was successful.</returns> </member> - <member name="M:NetSharp.Deprecated.Server.OnClientConnected(System.Net.EndPoint)"> + <member name="M:NetSharp.Deprecated.IClient.TryBindAsync(System.Net.IPAddress,System.Nullable{System.Int32},System.TimeSpan)"> <summary> - Invokes the <see cref="E:NetSharp.Deprecated.Server.ClientConnected"/> event. + Attempts to asynchronously bind the underlying socket to the given local address and port. Does not block. + If the timeout is exceeded the binding attempt is aborted and the method returns false. </summary> - <param name="remoteEndPoint">The remote endpoint with which a connection was made.</param> + <param name="localAddress">The local IP address to bind to. Null if any IP address will suffice.</param> + <param name="localPort">The local port to bind to. Null if any port will suffice.</param> + <param name="timeout">The timeout within which to attempt the binding.</param> + <returns>Whether the binding was successful or not.</returns> </member> - <member name="M:NetSharp.Deprecated.Server.OnClientDisconnected(System.Net.EndPoint)"> + <member name="M:NetSharp.Deprecated.IClient.TryConnectAsync(System.Net.IPAddress,System.Int32,System.TimeSpan)"> <summary> - Invokes the <see cref="E:NetSharp.Deprecated.Server.ClientDisconnected"/> event. + Attempts to connect asynchronously to the remote <see cref="T:NetSharp.Deprecated.Server"/> at the given <see cref="T:System.Net.IPAddress"/> + and over the given port. If the timeout is exceeded the connection attempt is aborted and the method returns false. </summary> - <param name="remoteEndPoint">The remote endpoint with which a connection was lost.</param> + <param name="remoteAddress">The remote IP address to connect to.</param> + <param name="remotePort">The remote port to connect over.</param> + <param name="timeout">The timeout within which to attempt the connection.</param> + <returns>Whether the connection was successful or not.</returns> </member> - <member name="M:NetSharp.Deprecated.Server.OnServerStarted"> + <member name="T:NetSharp.Deprecated.INetworkSerialisable"> <summary> - Invokes the <see cref="E:NetSharp.Deprecated.Server.ServerStarted"/> event. + Describes an object that can be serialised to be sent across the network. </summary> </member> - <member name="M:NetSharp.Deprecated.Server.OnServerStopped"> + <member name="M:NetSharp.Deprecated.INetworkSerialisable.Deserialise(System.ReadOnlyMemory{System.Byte})"> <summary> - Invokes the <see cref="E:NetSharp.Deprecated.Server.ServerStopped"/> event. + Deserialises the object instance from a byte array. </summary> + <param name="serialisedObject">The memory containing the serialised object instance.</param> </member> - <member name="M:NetSharp.Deprecated.Server.TryBind(System.Net.EndPoint,System.TimeSpan)"> + <member name="M:NetSharp.Deprecated.INetworkSerialisable.Serialise"> <summary> - Attempts to synchronously bind the underlying socket to the given local endpoint. Blocks. - If the timeout is exceeded the binding attempt is aborted and the method returns false. + Serialises the object instance into a byte array. </summary> - <param name="localEndPoint">The local endpoint to bind to.</param> - <param name="timeout">The timeout within which to attempt the binding.</param> - <returns>Whether the binding was successful or not.</returns> + <returns>The memory containing the serialised object instance.</returns> </member> - <member name="M:NetSharp.Deprecated.Server.TryBindAsync(System.Net.EndPoint,System.TimeSpan)"> + <member name="T:NetSharp.Deprecated.IPacket"> <summary> - Attempts to asynchronously bind the underlying socket to the given local endpoint. Does not block. - If the timeout is exceeded the binding attempt is aborted and the method returns false. + Describes the methods and properties that every packet </summary> - <param name="localEndPoint">The local endpoint to bind to.</param> - <param name="timeout">The timeout within which to attempt the binding.</param> - <returns>Whether the binding was successful or not.</returns> </member> - <member name="T:NetSharp.Deprecated.Server.ClientHandlerArgs"> + <member name="M:NetSharp.Deprecated.IPacket.AfterDeserialisation"> <summary> - Holds information about the arguments passed to every client handler task. + Allows for custom fields to be converted from their serialised format, after being received from the network. </summary> </member> - <member name="M:NetSharp.Deprecated.Server.ClientHandlerArgs.#ctor(System.Net.EndPoint,System.Net.Sockets.Socket)"> + <member name="M:NetSharp.Deprecated.IPacket.BeforeSerialisation"> <summary> - Initialises a new instance of the <see cref="T:NetSharp.Deprecated.Server.ClientHandlerArgs"/> struct. + Allows for custom fields to be converted into another format prior to being sent via the network. </summary> - <param name="remoteEndPoint">The remote endpoint of the client that should be handled.</param> - <param name="handlerSocket">The handler socket of the client that should be handled.</param> </member> - <member name="F:NetSharp.Deprecated.Server.ClientHandlerArgs.ClientEndPoint"> + <member name="T:NetSharp.Deprecated.IPacketHandler"> <summary> - The remote endpoint for the client being handled. + Describes a class capable of registering and deregistering packet handlers, and capable of + handling incoming packets according to the currently registered packet handlers. </summary> </member> - <member name="F:NetSharp.Deprecated.Server.ClientHandlerArgs.ClientSocket"> + <member name="M:NetSharp.Deprecated.IPacketHandler.TryDeregisterComplexPacketHandler``2(NetSharp.Deprecated.ComplexPacketHandler{``0,``1}@)"> <summary> - The client handler socket for the client being handled. Is only set if using TCP. + Attempts to deregister the complex packet handler delegate for all packets of the given type. If a handler + method doesn't exist for the given packet type, <paramref name="oldHandlerDelegate"/> will be <c>default</c>. </summary> + <typeparam name="Req">The type of request packet for which to deregister the handler delegate.</typeparam> + <typeparam name="Rep">The type of response packet that is generated by the delegate method.</typeparam> + <param name="oldHandlerDelegate">The old handler delegate method that was previously registered.</param> + <returns>Whether the packet handler delegate was successfully deregistered.</returns> </member> - <member name="M:NetSharp.Deprecated.Server.ClientHandlerArgs.ForTcpClientHandler(System.Net.Sockets.Socket@)"> + <member name="M:NetSharp.Deprecated.IPacketHandler.TryDeregisterSimplePacketHandler``1(NetSharp.Deprecated.SimplePacketHandler{``0}@)"> <summary> - Constructs a new instance of the <see cref="T:NetSharp.Deprecated.Server.ClientHandlerArgs"/> for a TCP client. + Attempts to deregister the simple packet handler delegate for all packets of the given type. If a handler + method doesn't exist for the given packet type, <paramref name="oldHandlerDelegate"/> will be <c>default</c>. </summary> - <returns>A new instance of the <see cref="T:NetSharp.Deprecated.Server.ClientHandlerArgs"/>, setup for a TCP client.</returns> + <typeparam name="Req">The type of request packet for which to deregister the handler delegate.</typeparam> + <param name="oldHandlerDelegate">The old handler delegate method that was previously registered.</param> + <returns>Whether the packet handler delegate was successfully deregistered.</returns> </member> - <member name="M:NetSharp.Deprecated.Server.ClientHandlerArgs.ForUdpClientHandler(System.Net.EndPoint@)"> + <member name="M:NetSharp.Deprecated.IPacketHandler.TryRegisterComplexPacketHandler``2(NetSharp.Deprecated.ComplexPacketHandler{``0,``1})"> <summary> - Constructs a new instance of the <see cref="T:NetSharp.Deprecated.Server.ClientHandlerArgs"/> for a UDP client. + Attempts to register a complex packet handler delegate for all packets of the given type. If a handler + method already exists for the given packet type, it will be updated and replaced with the given one. </summary> - <returns>A new instance of the <see cref="T:NetSharp.Deprecated.Server.ClientHandlerArgs"/>, setup for a UDP client.</returns> + <typeparam name="Req">The type of request packet for which to register the handler delegate.</typeparam> + <typeparam name="Rep">The type of response packet that is generated by the delegate method.</typeparam> + <param name="handlerDelegate">The delegate method to register as the complex packet handler.</param> + <returns>Whether the packet handler delegate was successfully registered.</returns> </member> - <member name="E:NetSharp.Deprecated.Server.ClientConnected"> + <member name="M:NetSharp.Deprecated.IPacketHandler.TryRegisterSimplePacketHandler``1(NetSharp.Deprecated.SimplePacketHandler{``0})"> <summary> - Signifies that a connection with a remote endpoint has been made. + Attempts to register a simple packet handler delegate for all packets of the given type. If a handler + method already exists for the given packet type, it will be updated and replaced with the given one. </summary> + <typeparam name="Req">The type of request packet for which to register the handler delegate.</typeparam> + <param name="handlerDelegate">The delegate method to register as the simple packet handler.</param> + <returns>Whether the packet handler delegate was successfully registered.</returns> </member> - <member name="E:NetSharp.Deprecated.Server.ClientDisconnected"> + <member name="T:NetSharp.Deprecated.IRequestPacket"> <summary> - Signifies that a connection with a remote endpoint has been lost. + Describes a request packet. </summary> </member> - <member name="E:NetSharp.Deprecated.Server.ServerStarted"> + <member name="T:NetSharp.Deprecated.IResponsePacket`1"> <summary> - Signifies that the server was started and clients will start being accepted. + Describes a response packet to a request packet. </summary> + <typeparam name="TReq">The request packet that this type is a response to.</typeparam> </member> - <member name="E:NetSharp.Deprecated.Server.ServerStopped"> + <member name="P:NetSharp.Deprecated.IResponsePacket`1.RequestPacket"> <summary> - Signifies that the server was stopped and clients will stop being accepted. + The request packet that was handled with this response packet. </summary> </member> - <member name="P:NetSharp.Deprecated.Server.NetworkOperationTimeout"> + <member name="T:NetSharp.Deprecated.IServer"> <summary> - The timeout value for network operations such as sending bytes or receiving bytes over the network. + Describes a server capable of asynchronously handling multiple <see cref="T:NetSharp.Deprecated.IClient"/> connections at once. </summary> </member> - <member name="P:NetSharp.Deprecated.Server.SocketOptions"> + <member name="E:NetSharp.Deprecated.IServer.ClientConnected"> <summary> - The configured socket options for the underlying connection. + Signifies that a connection with a remote endpoint has been made. </summary> </member> - <member name="M:NetSharp.Deprecated.Server.RunAsync(System.Net.EndPoint)"> - <inheritdoc /> - </member> - <member name="M:NetSharp.Deprecated.Server.Shutdown"> - <inheritdoc /> - </member> - <member name="M:NetSharp.Deprecated.Server.TryDeregisterComplexPacketHandler``2(NetSharp.Deprecated.ComplexPacketHandler{``0,``1}@)"> - <inheritdoc /> - </member> - <member name="M:NetSharp.Deprecated.Server.TryDeregisterSimplePacketHandler``1(NetSharp.Deprecated.SimplePacketHandler{``0}@)"> - <inheritdoc /> - </member> - <member name="M:NetSharp.Deprecated.Server.TryRegisterComplexPacketHandler``2(NetSharp.Deprecated.ComplexPacketHandler{``0,``1})"> - <inheritdoc /> - </member> - <member name="M:NetSharp.Deprecated.Server.TryRegisterSimplePacketHandler``1(NetSharp.Deprecated.SimplePacketHandler{``0})"> - <inheritdoc /> - </member> - <member name="T:NetSharp.Deprecated.ServerClientConnection"> + <member name="E:NetSharp.Deprecated.IServer.ClientDisconnected"> <summary> - Base class for connections, holding methods shared between the <see cref="T:NetSharp.Deprecated.Client"/> and <see cref="T:NetSharp.Deprecated.Server"/> classes. + Signifies that a connection with a remote endpoint has been lost. </summary> </member> - <member name="F:NetSharp.Deprecated.ServerClientConnection.logger"> + <member name="E:NetSharp.Deprecated.IServer.ServerStarted"> <summary> - The logger to which the server can log messages. + Signifies that the server was started and clients will start being accepted. </summary> </member> - <member name="M:NetSharp.Deprecated.ServerClientConnection.#ctor"> + <member name="E:NetSharp.Deprecated.IServer.ServerStopped"> <summary> - Initialises a new instance of the <see cref="T:NetSharp.Deprecated.ServerClientConnection"/> class. + Signifies that the server was stopped and clients will stop being accepted. </summary> </member> - <member name="M:NetSharp.Deprecated.ServerClientConnection.Dispose(System.Boolean)"> + <member name="M:NetSharp.Deprecated.IServer.RunAsync(System.Net.EndPoint)"> <summary> - Disposes of this <see cref="T:NetSharp.Deprecated.ServerClientConnection"/> instance. + Starts the server asynchronously and starts accepting client connections. Does not block. </summary> - <param name="disposing">Whether this instance is being disposed.</param> + <param name="localEndPoint">The local endpoint to bind to.</param> </member> - <member name="M:NetSharp.Deprecated.ServerClientConnection.OnBytesReceived(System.Net.EndPoint,System.Int32)"> + <member name="M:NetSharp.Deprecated.IServer.Shutdown"> <summary> - Invokes the <see cref="E:NetSharp.Deprecated.ServerClientConnection.BytesReceived"/> event. + Shuts down the server. </summary> - <param name="remoteEndPoint">The remote endpoint from which the bytes were received.</param> - <param name="bytesReceived">The number of bytes that were received from the remote endpoint.</param> </member> - <member name="M:NetSharp.Deprecated.ServerClientConnection.OnBytesSent(System.Net.EndPoint,System.Int32)"> + <member name="T:NetSharp.Deprecated.LogLevel"> <summary> - Invokes the <see cref="E:NetSharp.Deprecated.ServerClientConnection.BytesSent"/> event. + Specifies the severity level of a log message. </summary> - <param name="remoteEndPoint">The remote endpoint to which the bytes were sent.</param> - <param name="bytesSent">The number of bytes that were sent to the remote endpoint.</param> </member> - <member name="E:NetSharp.Deprecated.ServerClientConnection.BytesReceived"> + <member name="F:NetSharp.Deprecated.LogLevel.Info"> <summary> - Signifies that some data has been received from the remote endpoint. + The logged message contains some information. Lowest severity. </summary> </member> - <member name="E:NetSharp.Deprecated.ServerClientConnection.BytesSent"> + <member name="F:NetSharp.Deprecated.LogLevel.Warn"> <summary> - Signifies that some data was sent to the remote endpoint. + The logged message contains a warning. Higher severity. </summary> </member> - <member name="M:NetSharp.Deprecated.ServerClientConnection.ChangeLoggingStream(System.IO.Stream,NetSharp.Logging.LogLevel)"> + <member name="F:NetSharp.Deprecated.LogLevel.Error"> <summary> - Makes the client log to the given stream. + The logged message contains details about an error. Higher severity. </summary> - <param name="loggingStream">The stream that new messages should be logged to.</param> - <param name="minimumMessageSeverityLevel"> - The minimum severity level that new messages must have to be logged to the stream. - </param> - </member> - <member name="M:NetSharp.Deprecated.ServerClientConnection.Dispose"> - <inheritdoc /> </member> - <member name="T:NetSharp.Deprecated.ServerExtensions"> + <member name="F:NetSharp.Deprecated.LogLevel.Exception"> <summary> - Provides additional methods and functionality to the <see cref="T:NetSharp.Deprecated.Server"/> class. + The logged message contains details about an exception. Highest severity. </summary> </member> - <member name="M:NetSharp.Deprecated.ServerExtensions.Run(NetSharp.Deprecated.Server,System.Net.IPAddress,System.Int32)"> + <member name="T:NetSharp.Deprecated.Logger"> <summary> - Starts the server synchronously and starts accepting client connections. Blocks. + A simple logger capable of writing text to a stream. </summary> - <param name="instance">The instance on which this extension method should be called.</param> - <param name="localAddress">The local IP address to bind to.</param> - <param name="localPort">The local port to bind to.</param> </member> - <member name="M:NetSharp.Deprecated.ServerExtensions.Run(NetSharp.Deprecated.Server,System.Net.IPAddress)"> + <member name="F:NetSharp.Deprecated.Logger.loggingStream"> <summary> - Starts the server synchronously and starts accepting client connections. Blocks. Uses the default connection port. + The stream to which messages will be logged. </summary> - <param name="instance">The instance on which this extension method should be called.</param> - <param name="localAddress">The local IP address to bind to.</param> </member> - <member name="M:NetSharp.Deprecated.ServerExtensions.RunAsync(NetSharp.Deprecated.Server,System.Net.IPAddress)"> + <member name="F:NetSharp.Deprecated.Logger.minimumSeverity"> <summary> - Starts the server asynchronously and starts accepting client connections. Does not block. Uses the default - connection port. + The minimum severity that log messages need to be logged to the underlying stream. </summary> - <param name="instance">The instance on which this extension method should be called.</param> - <param name="localAddress">The local IP address to bind to.</param> </member> - <member name="M:NetSharp.Deprecated.ServerExtensions.RunAsync(NetSharp.Deprecated.Server,System.Net.IPAddress,System.Int32)"> + <member name="F:NetSharp.Deprecated.Logger.writer"> <summary> - Starts the server asynchronously and starts accepting client connections. Does not block. + The text writer we will use to log messages to the underlying stream. </summary> - <param name="instance">The instance on which this extension method should be called.</param> - <param name="localAddress">The local IP address to bind to.</param> - <param name="localPort">The local port to bind to.</param> </member> - <member name="T:NetSharp.Deprecated.SocketOptions"> + <member name="M:NetSharp.Deprecated.Logger.#ctor(System.IO.Stream,NetSharp.Deprecated.LogLevel)"> <summary> - Allows for manipulation of socket options. + Initialises a new instance of the <see cref="T:NetSharp.Deprecated.Logger"/> struct. </summary> + <param name="outputStream">The stream that the logger instance should log messages to.</param> + <param name="minimumLogSeverity">The minimum log level that will be logged to the output stream.</param> </member> - <member name="F:NetSharp.Deprecated.SocketOptions.managedSocket"> + <member name="M:NetSharp.Deprecated.Logger.Dispose"> + <inheritdoc /> + </member> + <member name="M:NetSharp.Deprecated.Logger.Log(System.String,System.Exception,NetSharp.Deprecated.LogLevel)"> <summary> - The <see cref="T:System.Net.Sockets.Socket"/> instance whose settings are being managed. + Logs a message to the underlying stream, along with the given exception and at the given severity. </summary> + <param name="message">The message that should be logged.</param> + <param name="exception">The exception that occurred (if any).</param> + <param name="severity">The severity of the message that is being logged.</param> </member> - <member name="M:NetSharp.Deprecated.SocketOptions.#ctor(System.Net.Sockets.Socket@)"> + <member name="M:NetSharp.Deprecated.Logger.LogAsync(System.String,System.Exception,NetSharp.Deprecated.LogLevel)"> <summary> - Initialises a new instance of the <see cref="T:NetSharp.Deprecated.SocketOptions"/> class. + Logs a message asynchronously to the underlying stream, along with the given exception and at the given severity. </summary> - <param name="socket">The <see cref="T:System.Net.Sockets.Socket"/> instance whose options should be managed.</param> + <param name="message">The message that should be logged.</param> + <param name="exception">The exception that occurred (if any).</param> + <param name="severity">The severity of the message that is being logged.</param> </member> - <member name="P:NetSharp.Deprecated.SocketOptions.DualMode"> + <member name="M:NetSharp.Deprecated.Logger.LogError(System.String)"> <summary> - Whether this <see cref="T:System.Net.Sockets.Socket"/> can operate in dual IPv4 / IPv6 mode. + Logs an error to the underlying stream, with severity <see cref="F:NetSharp.Deprecated.LogLevel.Info"/>. </summary> + <param name="message">The error that should be logged.</param> </member> - <member name="P:NetSharp.Deprecated.SocketOptions.ForceFlush"> + <member name="M:NetSharp.Deprecated.Logger.LogErrorAsync(System.String)"> <summary> - Whether sending a packet flushes underlying <see cref="T:System.Net.Sockets.NetworkStream"/>. + Logs an error to the underlying stream asynchronously, with severity <see cref="F:NetSharp.Deprecated.LogLevel.Error"/>. </summary> - <remarks> - This value is only used in a <see cref="T:System.Net.Sockets.TcpClient"/> instance, which uses a <see cref="T:System.Net.Sockets.NetworkStream"/> - to send and receive data. A <see cref="T:System.Net.Sockets.UdpClient"/> is unaffected by this value. - </remarks> + <param name="message">The error that should be logged.</param> </member> - <member name="P:NetSharp.Deprecated.SocketOptions.Fragment"> + <member name="M:NetSharp.Deprecated.Logger.LogException(System.Exception)"> <summary> - Whether this <see cref="T:System.Net.Sockets.Socket"/> is allowed to fragment frames that are too large to send in one go. + Logs an exception to the underlying stream, with severity <see cref="F:NetSharp.Deprecated.LogLevel.Exception"/>. </summary> + <param name="exception">The exception that should be logged.</param> </member> - <member name="P:NetSharp.Deprecated.SocketOptions.HopLimit"> + <member name="M:NetSharp.Deprecated.Logger.LogException(System.String,System.Exception)"> <summary> - The hop limit for packets sent by this <see cref="T:System.Net.Sockets.Socket"/>. Comparable to IPv4s TTL (Time To Live). + Logs an exception to the underlying stream, along with a short debug message, with severity + <see cref="F:NetSharp.Deprecated.LogLevel.Exception"/>. </summary> + <param name="message">The debug message that should be logged with the exception.</param> + <param name="exception">The exception that should be logged.</param> </member> - <member name="P:NetSharp.Deprecated.SocketOptions.IsChecksumEnabled"> + <member name="M:NetSharp.Deprecated.Logger.LogExceptionAsync(System.Exception)"> <summary> - Whether a checksum should be created for each UDP packet sent. + Logs an exception to the underlying stream asynchronously, with severity <see cref="F:NetSharp.Deprecated.LogLevel.Exception"/>. </summary> + <param name="exception">The exception that should be logged.</param> </member> - <member name="P:NetSharp.Deprecated.SocketOptions.IsRoutingEnabled"> + <member name="M:NetSharp.Deprecated.Logger.LogExceptionAsync(System.String,System.Exception)"> <summary> - Whether the packet should be sent directly to its destination or allowed to be routed through multiple destinations - first. + Logs an exception to the underlying stream asynchronously, along with a short debug message, with severity + <see cref="F:NetSharp.Deprecated.LogLevel.Exception"/>. </summary> + <param name="message">The debug message that should be logged with the exception.</param> + <param name="exception">The exception that should be logged.</param> </member> - <member name="P:NetSharp.Deprecated.SocketOptions.LocalEndPoint"> + <member name="M:NetSharp.Deprecated.Logger.LogMessage(System.String)"> <summary> - The local <see cref="T:System.Net.EndPoint"/> for the <see cref="F:NetSharp.Deprecated.SocketOptions.managedSocket"/>. + Logs a message to the underlying stream, with severity <see cref="F:NetSharp.Deprecated.LogLevel.Info"/>. </summary> + <param name="message">The message that should be logged.</param> </member> - <member name="P:NetSharp.Deprecated.SocketOptions.LocalIPEndPoint"> + <member name="M:NetSharp.Deprecated.Logger.LogMessageAsync(System.String)"> <summary> - The local <see cref="T:System.Net.IPEndPoint"/> for this <see cref="T:System.Net.Sockets.Socket"/> instance. + Logs a message to the underlying stream asynchronously, with severity <see cref="F:NetSharp.Deprecated.LogLevel.Info"/>. </summary> + <param name="message">The message that should be logged.</param> </member> - <member name="P:NetSharp.Deprecated.SocketOptions.RemoteEndPoint"> + <member name="M:NetSharp.Deprecated.Logger.LogWarning(System.String)"> <summary> - The remote <see cref="T:System.Net.EndPoint"/> for the <see cref="F:NetSharp.Deprecated.SocketOptions.managedSocket"/>. + Logs a warning to the underlying stream, with severity <see cref="F:NetSharp.Deprecated.LogLevel.Info"/>. </summary> + <param name="message">The warning that should be logged.</param> </member> - <member name="P:NetSharp.Deprecated.SocketOptions.RemoteIPEndPoint"> + <member name="M:NetSharp.Deprecated.Logger.LogWarningAsync(System.String)"> <summary> - The remote <see cref="T:System.Net.IPEndPoint"/> that this <see cref="T:System.Net.Sockets.Socket"/> instance communicates with. + Logs a warning to the underlying stream asynchronously, with severity <see cref="F:NetSharp.Deprecated.LogLevel.Warn"/>. </summary> + <param name="message">The warning that should be logged.</param> </member> - <member name="P:NetSharp.Deprecated.SocketOptions.Ttl"> + <member name="T:NetSharp.Deprecated.NetworkErrorCode"> <summary> - The 'Time To Live' for this <see cref="T:System.Net.Sockets.Socket"/>. + Enumerates the possible error codes for network operations, being held in the packet. </summary> </member> - <member name="P:NetSharp.Deprecated.SocketOptions.UseLoopback"> + <member name="F:NetSharp.Deprecated.NetworkErrorCode.Ok"> <summary> - Whether this <see cref="T:System.Net.Sockets.Socket"/> should use a loopback address and bypass hardware. + Signifies that there was no error during transmission. </summary> </member> - <member name="T:NetSharp.Deprecated.TcpClient"> + <member name="F:NetSharp.Deprecated.NetworkErrorCode.Error"> <summary> - Provides methods for TCP communication with a connected <see cref="T:NetSharp.Deprecated.TcpServer"/> instance. + A generic error occurred during packet transmission. </summary> </member> - <member name="M:NetSharp.Deprecated.TcpClient.#ctor"> - <inheritdoc /> - </member> - <member name="M:NetSharp.Deprecated.TcpClient.SendBytesAsync(System.Byte[],System.TimeSpan)"> - <inheritdoc /> - </member> - <member name="M:NetSharp.Deprecated.TcpClient.SendBytesWithResponseAsync(System.Byte[],System.TimeSpan)"> - <inheritdoc /> - </member> - <member name="M:NetSharp.Deprecated.TcpClient.SendComplexAsync``2(``0,System.TimeSpan)"> - <inheritdoc /> - </member> - <member name="M:NetSharp.Deprecated.TcpClient.SendSimpleAsync``1(``0,System.TimeSpan)"> - <inheritdoc /> - </member> - <member name="T:NetSharp.Deprecated.TcpServer"> + <member name="T:NetSharp.Deprecated.NetworkPacket"> <summary> - Provides methods for TCP communication with connected <see cref="T:NetSharp.Deprecated.TcpClient"/> instances. + Represents a low-level packet that is transmitted over the network. </summary> </member> - <member name="M:NetSharp.Deprecated.TcpServer.HandleClientAsync(NetSharp.Deprecated.Server.ClientHandlerArgs,System.Threading.CancellationToken)"> - <inheritdoc /> - </member> - <member name="M:NetSharp.Deprecated.TcpServer.#ctor(System.TimeSpan)"> - <inheritdoc /> - </member> - <member name="M:NetSharp.Deprecated.TcpServer.#ctor"> - <inheritdoc /> - </member> - <member name="M:NetSharp.Deprecated.TcpServer.RunAsync(System.Net.EndPoint)"> - <inheritdoc /> - </member> - <member name="T:NetSharp.Deprecated.TcpSocketOptions"> + <member name="M:NetSharp.Deprecated.NetworkPacket.#ctor(System.ReadOnlyMemory{System.Byte},NetSharp.Deprecated.NetworkPacketHeader,NetSharp.Deprecated.NetworkPacketFooter)"> <summary> - Allows for manipulation of TCP socket options. + Initialises a new instance of the <see cref="T:NetSharp.Deprecated.NetworkPacket"/> struct. </summary> + <param name="data">The data that should be transmitted in the packet.</param> + <param name="header">The header for the packet.</param> + <param name="footer">The footer for the packet.</param> </member> - <member name="M:NetSharp.Deprecated.TcpSocketOptions.#ctor(System.Net.Sockets.Socket@)"> - <inheritdoc /> - </member> - <member name="P:NetSharp.Deprecated.TcpSocketOptions.HopLimit"> - <inheritdoc /> - </member> - <member name="P:NetSharp.Deprecated.TcpSocketOptions.IsRoutingEnabled"> - <inheritdoc /> - </member> - <member name="P:NetSharp.Deprecated.TcpSocketOptions.UseLoopback"> - <inheritdoc /> - </member> - <member name="T:NetSharp.Deprecated.UdpClient"> + <member name="F:NetSharp.Deprecated.NetworkPacket.DataSegmentSize"> <summary> - Provides methods for UDP communication with a connected <see cref="T:NetSharp.Deprecated.UdpServer"/> instance. + The number of bytes allocated in each packet for user data. </summary> </member> - <member name="M:NetSharp.Deprecated.UdpClient.#ctor"> - <inheritdoc /> - </member> - <member name="M:NetSharp.Deprecated.UdpClient.SendBytesAsync(System.Byte[],System.TimeSpan)"> - <inheritdoc /> - </member> - <member name="M:NetSharp.Deprecated.UdpClient.SendBytesWithResponseAsync(System.Byte[],System.TimeSpan)"> - <inheritdoc /> - </member> - <member name="M:NetSharp.Deprecated.UdpClient.SendComplexAsync``2(``0,System.TimeSpan)"> - <inheritdoc /> - </member> - <member name="M:NetSharp.Deprecated.UdpClient.SendSimpleAsync``1(``0,System.TimeSpan)"> - <inheritdoc /> - </member> - <member name="T:NetSharp.Deprecated.UdpServer"> + <member name="F:NetSharp.Deprecated.NetworkPacket.FooterSize"> <summary> - Provides methods for UDP communication with connected <see cref="T:NetSharp.Deprecated.UdpClient"/> instances. + The number of bytes taken up in each packet by its footer. </summary> </member> - <member name="F:NetSharp.Deprecated.UdpServer.clientChannelOptions"> + <member name="F:NetSharp.Deprecated.NetworkPacket.HeaderSize"> <summary> - The options that should be applied to every channel created to handle a client. + The number of bytes taken up in each packet by its header. </summary> </member> - <member name="F:NetSharp.Deprecated.UdpServer.activeClients"> + <member name="F:NetSharp.Deprecated.NetworkPacket.PacketSize"> <summary> - Holds currently connected and active clients, as well as their current received packet queues. + The size of each packet, including its header, footer, and data segment. </summary> </member> - <member name="M:NetSharp.Deprecated.UdpServer.HandleClientAsync(NetSharp.Deprecated.Server.ClientHandlerArgs,System.Threading.CancellationToken)"> - <inheritdoc /> - </member> - <member name="M:NetSharp.Deprecated.UdpServer.#ctor(System.TimeSpan)"> - <inheritdoc /> - </member> - <member name="M:NetSharp.Deprecated.UdpServer.#ctor"> - <inheritdoc /> - </member> - <member name="M:NetSharp.Deprecated.UdpServer.RunAsync(System.Net.EndPoint)"> - <inheritdoc /> - </member> - <member name="T:NetSharp.Deprecated.UdpSocketOptions"> + <member name="F:NetSharp.Deprecated.NetworkPacket.DataBuffer"> <summary> - Allows for manipulation of UDP socket options. + The data held in this packet. </summary> </member> - <member name="M:NetSharp.Deprecated.UdpSocketOptions.#ctor(System.Net.Sockets.Socket@)"> - <inheritdoc /> + <member name="M:NetSharp.Deprecated.NetworkPacket.#ctor(System.ReadOnlyMemory{System.Byte},System.Int32,System.UInt32,NetSharp.Deprecated.NetworkErrorCode,System.Boolean)"> + <summary> + Initialises a new instance of the <see cref="T:NetSharp.Deprecated.NetworkPacket"/> struct. + </summary> + <param name="data">The data that should be transmitted in the packet.</param> + <param name="dataLength">The number of bytes that are held in the given data buffer.</param> + <param name="type">The packet type.</param> + <param name="errorCode">The error code associated with this transmission.</param> + <param name="hasSucceedingPacket">Whether this packet has a succeeding packet in the packet chain.</param> </member> - <member name="P:NetSharp.Deprecated.UdpSocketOptions.HopLimit"> - <inheritdoc /> + <member name="M:NetSharp.Deprecated.NetworkPacket.Deserialise(System.Memory{System.Byte})"> + <summary> + Deserialises the given buffer into a packet instance. + </summary> + <param name="buffer">The byte buffer to serialise.</param> + <returns>The deserialised packet instance.</returns> </member> - <member name="P:NetSharp.Deprecated.UdpSocketOptions.IsRoutingEnabled"> - <inheritdoc /> + <member name="M:NetSharp.Deprecated.NetworkPacket.Serialise(NetSharp.Deprecated.NetworkPacket)"> + <summary> + Serialises the given packet instance to a new byte buffer. + </summary> + <param name="instance">The packet instance to serialise.</param> + <returns>The byte buffer that represents the packet instance.</returns> </member> - <member name="P:NetSharp.Deprecated.UdpSocketOptions.UseLoopback"> - <inheritdoc /> + <member name="M:NetSharp.Deprecated.NetworkPacket.SerialiseToBuffer(System.Memory{System.Byte},NetSharp.Deprecated.NetworkPacket)"> + <summary> + Serialises the given packet instance into the given byte buffer. + </summary> + <param name="buffer"> + The buffer to which the instance should be serialised. Must be at least of size <see cref="F:NetSharp.Deprecated.NetworkPacket.PacketSize"/>. + </param> + <param name="instance">The packet instance to serialise.</param> + <exception cref="T:System.ArgumentException">Thrown if the given buffer is too small.</exception> </member> - <member name="T:NetSharp.Extensions.ConnectionBuilderExtensions"> + <member name="F:NetSharp.Deprecated.NetworkPacketFooter.Size"> <summary> - Provides additional methods and functionality to the <see cref="T:NetSharp.ConnectionBuilder"/> class. + The number of bytes taken up by a packet footer. </summary> </member> - <member name="T:NetSharp.Extensions.ConnectionExtensions"> + <member name="F:NetSharp.Deprecated.NetworkPacketHeader.Size"> <summary> - Provides additional methods and functionality to the <see cref="T:NetSharp.Connection"/> class. + The number of bytes taken up by a packet header. </summary> </member> - <member name="M:NetSharp.Extensions.ConnectionExtensions.TryBind(NetSharp.Connection,System.Net.EndPoint,System.TimeSpan)"> + <member name="F:NetSharp.Deprecated.NetworkPacketHeader.DataLength"> <summary> - Attempts to synchronously bind the underlying socket to the given local endpoint. Blocks. - If the timeout is exceeded the binding attempt is aborted and the method returns false. + The number of bytes of data held in the packet. </summary> - <param name="localEndPoint">The local endpoint to bind to.</param> - <param name="timeout">The timeout within which to attempt the binding.</param> - <returns>Whether the binding was successful or not.</returns> </member> - <member name="T:NetSharp.Logging.LogLevel"> + <member name="F:NetSharp.Deprecated.NetworkPacketHeader.ErrorCode"> <summary> - Specifies the severity level of a log message. + The error code for this packet. </summary> </member> - <member name="F:NetSharp.Logging.LogLevel.Info"> + <member name="F:NetSharp.Deprecated.NetworkPacketHeader.Type"> <summary> - The logged message contains some information. Lowest severity. + The packet type. </summary> </member> - <member name="F:NetSharp.Logging.LogLevel.Warn"> + <member name="T:NetSharp.Deprecated.PacketPipeline`3"> <summary> - The logged message contains a warning. Higher severity. + Represents a pipeline of transformations that packets must undergo. </summary> + <typeparam name="TInput">The type of packet the pipeline receives.</typeparam> + <typeparam name="TIntermediate">The type of packet the pipeline internally handles.</typeparam> + <typeparam name="TOutput">The type of packet the pipeline outputs.</typeparam> </member> - <member name="F:NetSharp.Logging.LogLevel.Error"> + <member name="M:NetSharp.Deprecated.PacketPipeline`3.ProcessPacket(`0)"> <summary> - The logged message contains details about an error. Higher severity. + Passes the given packet through the pipeline. </summary> + <param name="inputPacket">The incoming packet.</param> + <returns>The outgoing transformed packet.</returns> </member> - <member name="F:NetSharp.Logging.LogLevel.Exception"> + <member name="T:NetSharp.Deprecated.PacketPipelineStage`2"> <summary> - The logged message contains details about an exception. Highest severity. + Represents a single transformation applied to a packet traveling through the pipeline. </summary> + <typeparam name="TInput">The type the transformation takes as input.</typeparam> + <typeparam name="TOutput">The type the transformation produces as output.</typeparam> </member> - <member name="T:NetSharp.Logging.Logger"> + <member name="T:NetSharp.Deprecated.PacketPipelineBuilder`3"> <summary> - A simple logger capable of writing text to a stream. + Allows for configuring and subsequently building a <see cref="T:NetSharp.Deprecated.PacketPipeline`3"/> instance. </summary> + <typeparam name="TInput">The type of packet that will be submitted to the pipeline.</typeparam> + <typeparam name="TIntermediate">The type of packet that will be handled internally by the pipeline.</typeparam> + <typeparam name="TOutput">The type of packet that will be requested from the pipeline.</typeparam> </member> - <member name="F:NetSharp.Logging.Logger.loggingStream"> + <member name="M:NetSharp.Deprecated.PacketPipelineBuilder`3.Build"> <summary> - The stream to which messages will be logged. + Returns the currently configured <see cref="T:NetSharp.Deprecated.PacketPipeline`3"/> instance. </summary> + <returns>The configured <see cref="T:NetSharp.Deprecated.PacketPipeline`3"/> instance.</returns> + <exception cref="T:System.ArgumentNullException"> + Thrown when either <see cref="M:NetSharp.Deprecated.PacketPipelineBuilder`3.WithInputStage(System.Func{`0,`1}@)"/> or <see cref="M:NetSharp.Deprecated.PacketPipelineBuilder`3.WithOutputStage(System.Func{`1,`2}@)"/> have not been called. + </exception> </member> - <member name="F:NetSharp.Logging.Logger.minimumSeverity"> + <member name="M:NetSharp.Deprecated.PacketPipelineBuilder`3.WithInputStage(System.Func{`0,`1}@)"> <summary> - The minimum severity that log messages need to be logged to the underlying stream. + Configures the input stage for the pipeline. </summary> + <param name="stage"> + The transformation that should be applied to incoming packets, to convert them from the <typeparamref name="TInput"/> + type to the <typeparamref name="TIntermediate"/> type that the pipeline handles internally. + </param> + <returns>The builder instance for further configuration.</returns> </member> - <member name="F:NetSharp.Logging.Logger.writer"> + <member name="M:NetSharp.Deprecated.PacketPipelineBuilder`3.WithIntermediateStage(System.Func{`1,`1}@)"> <summary> - The text writer we will use to log messages to the underlying stream. + Adds the given intermediate stage to the pipeline. </summary> + <param name="stage"> + The transformation that should be applied to packets traveling through the pipeline. + </param> + <returns>The builder instance for further configuration.</returns> </member> - <member name="M:NetSharp.Logging.Logger.#ctor(System.IO.Stream,NetSharp.Logging.LogLevel)"> + <member name="M:NetSharp.Deprecated.PacketPipelineBuilder`3.WithOutputStage(System.Func{`1,`2}@)"> <summary> - Initialises a new instance of the <see cref="T:NetSharp.Logging.Logger"/> struct. + Configures the output stage for the pipeline. </summary> - <param name="outputStream">The stream that the logger instance should log messages to.</param> - <param name="minimumLogSeverity">The minimum log level that will be logged to the output stream.</param> - </member> - <member name="M:NetSharp.Logging.Logger.Dispose"> - <inheritdoc /> + <param name="stage"> + The transformation that should be applied to outgoing packets, to convert them from the + <typeparamref name="TIntermediate"/> type used internally to the <typeparamref name="TOutput"/> type. + </param> + <returns>The builder instance for further configuration.</returns> </member> - <member name="M:NetSharp.Logging.Logger.Log(System.String,System.Exception,NetSharp.Logging.LogLevel)"> + <member name="T:NetSharp.Deprecated.PacketRegistry"> <summary> - Logs a message to the underlying stream, along with the given exception and at the given severity. + Provides method of registering request packets and their relevant response packets, as well as mapping their ids. </summary> - <param name="message">The message that should be logged.</param> - <param name="exception">The exception that occurred (if any).</param> - <param name="severity">The severity of the message that is being logged.</param> </member> - <member name="M:NetSharp.Logging.Logger.LogAsync(System.String,System.Exception,NetSharp.Logging.LogLevel)"> + <member name="F:NetSharp.Deprecated.PacketRegistry.AutomaticPacketTypeIdStartPoint"> <summary> - Logs a message asynchronously to the underlying stream, along with the given exception and at the given severity. + The start id for automatically generated packet type ids. Any custom packet type ids lower than this value + that come from external assemblies will be incremented by this value, to ensure that there are no clashes. </summary> - <param name="message">The message that should be logged.</param> - <param name="exception">The exception that occurred (if any).</param> - <param name="severity">The severity of the message that is being logged.</param> </member> - <member name="M:NetSharp.Logging.Logger.LogError(System.String)"> + <member name="F:NetSharp.Deprecated.PacketRegistry.currentAutomaticPacketTypeIdCounterLockObject"> <summary> - Logs an error to the underlying stream, with severity <see cref="F:NetSharp.Logging.LogLevel.Info"/>. + The lock object for synchronising access to the <see cref="F:NetSharp.Deprecated.PacketRegistry.currentAutomaticPacketTypeIdCounter"/> field. </summary> - <param name="message">The error that should be logged.</param> </member> - <member name="M:NetSharp.Logging.Logger.LogErrorAsync(System.String)"> + <member name="F:NetSharp.Deprecated.PacketRegistry.idToPacketTypeMap"> <summary> - Logs an error to the underlying stream asynchronously, with severity <see cref="F:NetSharp.Logging.LogLevel.Error"/>. + Maps a packet type id to its relevant packet type, and vice-versa. </summary> - <param name="message">The error that should be logged.</param> </member> - <member name="M:NetSharp.Logging.Logger.LogException(System.Exception)"> + <member name="F:NetSharp.Deprecated.PacketRegistry.LibraryAssembly"> <summary> - Logs an exception to the underlying stream, with severity <see cref="F:NetSharp.Logging.LogLevel.Exception"/>. + The assembly that represents the library, where all of the builtin packets are defined. </summary> - <param name="exception">The exception that should be logged.</param> </member> - <member name="M:NetSharp.Logging.Logger.LogException(System.String,System.Exception)"> + <member name="F:NetSharp.Deprecated.PacketRegistry.requestToResponseMap"> <summary> - Logs an exception to the underlying stream, along with a short debug message, with severity - <see cref="F:NetSharp.Logging.LogLevel.Exception"/>. + Maps a request packet to its relevant response packet, and vice-versa. </summary> - <param name="message">The debug message that should be logged with the exception.</param> - <param name="exception">The exception that should be logged.</param> </member> - <member name="M:NetSharp.Logging.Logger.LogExceptionAsync(System.Exception)"> + <member name="F:NetSharp.Deprecated.PacketRegistry.currentAutomaticPacketTypeIdCounter"> <summary> - Logs an exception to the underlying stream asynchronously, with severity <see cref="F:NetSharp.Logging.LogLevel.Exception"/>. + The current id for registered packets. </summary> - <param name="exception">The exception that should be logged.</param> </member> - <member name="M:NetSharp.Logging.Logger.LogExceptionAsync(System.String,System.Exception)"> + <member name="M:NetSharp.Deprecated.PacketRegistry.GetNewPacketTypeId(System.Type)"> <summary> - Logs an exception to the underlying stream asynchronously, along with a short debug message, with severity - <see cref="F:NetSharp.Logging.LogLevel.Exception"/>. + Fetches the packet type id of the given packet type. If the packet type is declared outside of the library + assembly, then its value is incremented by the <see cref="F:NetSharp.Deprecated.PacketRegistry.AutomaticPacketTypeIdStartPoint"/> value. This ensure that + there are no clashes between the packet type ids of packets declared in the library and external packets. </summary> - <param name="message">The debug message that should be logged with the exception.</param> - <param name="exception">The exception that should be logged.</param> + <param name="packetType">The packet type whose id should be fetched.</param> + <returns>The id of the given packet type.</returns> </member> - <member name="M:NetSharp.Logging.Logger.LogMessage(System.String)"> + <member name="M:NetSharp.Deprecated.PacketRegistry.DeregisterPacketType(System.Type,System.Type)"> <summary> - Logs a message to the underlying stream, with severity <see cref="F:NetSharp.Logging.LogLevel.Info"/>. + Deregisters the given packet type from the registry. </summary> - <param name="message">The message that should be logged.</param> + <param name="requestPacketType">The request packet type to deregister, if it is registered.</param> + <param name="responsePacketType">The response packet associated with the request packet.</param> </member> - <member name="M:NetSharp.Logging.Logger.LogMessageAsync(System.String)"> + <member name="M:NetSharp.Deprecated.PacketRegistry.DeregisterPacketTypes(System.Collections.Generic.Dictionary{System.Type,System.Type})"> <summary> - Logs a message to the underlying stream asynchronously, with severity <see cref="F:NetSharp.Logging.LogLevel.Info"/>. + Deregisters the given packet types from the registry. </summary> - <param name="message">The message that should be logged.</param> + <param name="requestToResponsePacketTypeMap">The list of packet types to deregister, if they are registered.</param> </member> - <member name="M:NetSharp.Logging.Logger.LogWarning(System.String)"> + <member name="M:NetSharp.Deprecated.PacketRegistry.GetPacketId(System.Type)"> <summary> - Logs a warning to the underlying stream, with severity <see cref="F:NetSharp.Logging.LogLevel.Info"/>. + Returns the packet type id associated with the given packet type. </summary> - <param name="message">The warning that should be logged.</param> + <param name="packetType">The packet type whose id to fetch.</param> + <returns>The id of the packet type given.</returns> </member> - <member name="M:NetSharp.Logging.Logger.LogWarningAsync(System.String)"> + <member name="M:NetSharp.Deprecated.PacketRegistry.GetPacketId``1"> <summary> - Logs a warning to the underlying stream asynchronously, with severity <see cref="F:NetSharp.Logging.LogLevel.Warn"/>. + Returns the packet type id associated with the given packet type. </summary> - <param name="message">The warning that should be logged.</param> + <typeparam name="TPacket">The packet type whose id to fetch.</typeparam> + <returns>The id of the packet type given.</returns> </member> - <member name="T:NetSharp.Packets.Builtin.ConnectPacket"> + <member name="M:NetSharp.Deprecated.PacketRegistry.GetPacketType(System.UInt32)"> <summary> - A simple connection request packet for the UDP protocol. + Returns the packet type associated with the given id. </summary> + <param name="packetTypeId">The packet id whose mapped type to fetch.</param> + <returns>The packet type mapped by the given id.</returns> </member> - <member name="M:NetSharp.Packets.Builtin.ConnectPacket.AfterDeserialisation"> - <inheritdoc /> - </member> - <member name="M:NetSharp.Packets.Builtin.ConnectPacket.BeforeSerialisation"> - <inheritdoc /> - </member> - <member name="M:NetSharp.Packets.Builtin.ConnectPacket.Deserialise(System.ReadOnlyMemory{System.Byte})"> - <inheritdoc /> - </member> - <member name="M:NetSharp.Packets.Builtin.ConnectPacket.Serialise"> - <inheritdoc /> - </member> - <member name="T:NetSharp.Packets.Builtin.ConnectResponsePacket"> + <member name="M:NetSharp.Deprecated.PacketRegistry.GetRequestPacketType``1"> <summary> - A response packet for the <see cref="T:NetSharp.Packets.Builtin.ConnectPacket"/>. + Returns the type of request packet mapped by the given response packet type. </summary> + <typeparam name="TResponse">The response packet type whose request packet type to fetch.</typeparam> + <returns>The request packet type, <c>null</c> if no type is mapped.</returns> </member> - <member name="P:NetSharp.Packets.Builtin.ConnectResponsePacket.RequestPacket"> - <inheritdoc /> - </member> - <member name="M:NetSharp.Packets.Builtin.ConnectResponsePacket.AfterDeserialisation"> - <inheritdoc /> - </member> - <member name="M:NetSharp.Packets.Builtin.ConnectResponsePacket.BeforeSerialisation"> - <inheritdoc /> - </member> - <member name="M:NetSharp.Packets.Builtin.ConnectResponsePacket.Deserialise(System.ReadOnlyMemory{System.Byte})"> - <inheritdoc /> - </member> - <member name="M:NetSharp.Packets.Builtin.ConnectResponsePacket.Serialise"> - <inheritdoc /> - </member> - <member name="T:NetSharp.Packets.Builtin.DataPacket"> + <member name="M:NetSharp.Deprecated.PacketRegistry.GetRequestPacketType(System.Type)"> <summary> - A simple data transfer packet, that allows for the transmission of an arbitrary number of frames. + Returns the type of request packet mapped by the given response packet type. </summary> + <param name="responsePacketType">The response packet type whose request packet type to fetch.</param> + <returns>The request packet type, <c>null</c> if no type is mapped.</returns> </member> - <member name="F:NetSharp.Packets.Builtin.DataPacket.RequestBuffer"> + <member name="M:NetSharp.Deprecated.PacketRegistry.GetResponsePacketType``1"> <summary> - The data that should be transferred across the network. + Returns the type of response packet mapped by the given request packet type. </summary> + <typeparam name="TRequest">The request packet type whose response packet type to fetch.</typeparam> + <returns>The response packet type, <c>null</c> if no type is mapped.</returns> </member> - <member name="M:NetSharp.Packets.Builtin.DataPacket.#ctor"> + <member name="M:NetSharp.Deprecated.PacketRegistry.GetResponsePacketType(System.Type)"> <summary> - Initialises a new instance of the <see cref="T:NetSharp.Packets.Builtin.DataPacket"/> class. + Returns the type of response packet mapped by the given request packet type. </summary> + <param name="requestPacketType">The request packet type whose response packet type to fetch.</param> + <returns>The response packet type, <c>null</c> if no type is mapped.</returns> </member> - <member name="M:NetSharp.Packets.Builtin.DataPacket.#ctor(System.Memory{System.Byte})"> + <member name="M:NetSharp.Deprecated.PacketRegistry.RegisterPacketSourceAssemblies(System.Reflection.Assembly[])"> <summary> - Initialises a new instance of the <see cref="T:NetSharp.Packets.Builtin.DataPacket"/> class. + Rebuilds the packet registry, by registering every <see cref="T:NetSharp.Deprecated.IPacket"/> inheritor in the given assemblies. </summary> - <param name="buffer">The data that this request packet should contain.</param> - </member> - <member name="M:NetSharp.Packets.Builtin.DataPacket.AfterDeserialisation"> - <inheritdoc /> - </member> - <member name="M:NetSharp.Packets.Builtin.DataPacket.BeforeSerialisation"> - <inheritdoc /> - </member> - <member name="M:NetSharp.Packets.Builtin.DataPacket.Deserialise(System.ReadOnlyMemory{System.Byte})"> - <inheritdoc /> - </member> - <member name="M:NetSharp.Packets.Builtin.DataPacket.Serialise"> - <inheritdoc /> + <param name="packetSourceAssemblies"> + The assemblies from which the packet types to register are sourced. + </param> </member> - <member name="T:NetSharp.Packets.Builtin.DataResponsePacket"> + <member name="M:NetSharp.Deprecated.PacketRegistry.RegisterPacketSourceAssembly(System.Reflection.Assembly)"> <summary> - A response packet for the <see cref="T:NetSharp.Packets.Builtin.DataPacket"/>. + Registers all the <see cref="T:NetSharp.Deprecated.IPacket"/> implementors in the given assembly. </summary> + <param name="packetSourceAssembly">The assembly whose packet types to register.</param> </member> - <member name="F:NetSharp.Packets.Builtin.DataResponsePacket.ResponseBuffer"> + <member name="M:NetSharp.Deprecated.PacketRegistry.RegisterPacketType(System.Type,System.Type)"> <summary> - The data that should be transferred across the network. + Registers the given packet type to the registry. </summary> + <param name="requestPacketType">The request packet type to register, if it is not registered.</param> + <param name="responsePacketType">The response packet associated with the request packet.</param> </member> - <member name="M:NetSharp.Packets.Builtin.DataResponsePacket.#ctor"> + <member name="M:NetSharp.Deprecated.PacketRegistry.RegisterPacketTypes(System.Collections.Generic.Dictionary{System.Type,System.Type})"> <summary> - Initialises a new instance of the <see cref="T:NetSharp.Packets.Builtin.DataResponsePacket"/> class. + Registers the given packet types to the registry. </summary> + <param name="requestToResponsePacketTypeMap"> + The dictionary mapping the request packet types to register, to their relevant response packet types. + The response packet type can be null; then the request packet type is treated as a 'simple' packet. + </param> </member> - <member name="M:NetSharp.Packets.Builtin.DataResponsePacket.#ctor(System.Memory{System.Byte})"> + <member name="M:NetSharp.Deprecated.PacketRegistry.#cctor"> <summary> - Initialises a new instance of the <see cref="T:NetSharp.Packets.Builtin.DataResponsePacket"/> class. + Initialises a new instance of the <see cref="T:NetSharp.Deprecated.PacketRegistry"/> class. </summary> - <param name="buffer">The data that this response packet should contain.</param> </member> - <member name="P:NetSharp.Packets.Builtin.DataResponsePacket.RequestPacket"> - <inheritdoc /> - </member> - <member name="M:NetSharp.Packets.Builtin.DataResponsePacket.AfterDeserialisation"> - <inheritdoc /> - </member> - <member name="M:NetSharp.Packets.Builtin.DataResponsePacket.BeforeSerialisation"> - <inheritdoc /> - </member> - <member name="M:NetSharp.Packets.Builtin.DataResponsePacket.Deserialise(System.ReadOnlyMemory{System.Byte})"> - <inheritdoc /> + <member name="T:NetSharp.Deprecated.PacketTypeIdAttribute"> + <summary> + Allows the placing of a custom packet type on a class or struct. This is used if the class or struct + inherits from <see cref="T:NetSharp.Deprecated.IRequestPacket"/> or <see cref="T:NetSharp.Deprecated.IResponsePacket`1"/>. + </summary> </member> - <member name="M:NetSharp.Packets.Builtin.DataResponsePacket.Serialise"> - <inheritdoc /> + <member name="M:NetSharp.Deprecated.PacketTypeIdAttribute.#ctor(System.UInt32)"> + <summary> + Initialises a new instance of the <see cref="T:NetSharp.Deprecated.PacketTypeIdAttribute"/> attribute. + </summary> + <param name="type">The custom type id that the decorated packet type should have.</param> </member> - <member name="T:NetSharp.Packets.Builtin.DisconnectPacket"> + <member name="P:NetSharp.Deprecated.PacketTypeIdAttribute.Id"> <summary> - A simple disconnect packet for the UDP protocol. + The custom type id that the decorated packet type should have. This overrides the automatically generated id. </summary> </member> - <member name="M:NetSharp.Packets.Builtin.DisconnectPacket.AfterDeserialisation"> + <member name="M:NetSharp.Deprecated.RemoteSocketClient.Dispose"> <inheritdoc /> </member> - <member name="M:NetSharp.Packets.Builtin.DisconnectPacket.BeforeSerialisation"> - <inheritdoc /> + <member name="M:NetSharp.Deprecated.RemoteSocketClient.Dispose(System.Boolean)"> + <summary> + Implementation of dispose pattern. + </summary> + <param name="disposing"> + Whether this method is being called by the object finalizer, or by the <see cref="M:NetSharp.Deprecated.RemoteSocketClient.Dispose"/> method. + </param> </member> - <member name="M:NetSharp.Packets.Builtin.DisconnectPacket.Deserialise(System.ReadOnlyMemory{System.Byte})"> - <inheritdoc /> + <member name="M:NetSharp.Deprecated.SerialisedPacket.From``1(``0)"> + <summary> + Serialises the given serialisable packet instance and returns the <see cref="T:NetSharp.Deprecated.SerialisedPacket"/> instance + that was generated. This method invokes <see cref="M:NetSharp.Deprecated.IPacket.BeforeSerialisation"/>. + </summary> + <typeparam name="T">The packet type that will be serialised.</typeparam> + <param name="serialisable">The packet instance that should be serialised.</param> + <returns>The serialised instance.</returns> </member> - <member name="M:NetSharp.Packets.Builtin.DisconnectPacket.Serialise"> - <inheritdoc /> + <member name="M:NetSharp.Deprecated.SerialisedPacket.To``1(NetSharp.Deprecated.SerialisedPacket@)"> + <summary> + Deserialises and returns a packet instance of the given type from the <see cref="T:NetSharp.Deprecated.SerialisedPacket"/> instance + that was given. This method invokes <see cref="M:NetSharp.Deprecated.IPacket.AfterDeserialisation"/>. + </summary> + <typeparam name="T">The packet type to which the packet should be deserialised.</typeparam> + <param name="instance">The serialised packet instance that should be deserialised.</param> + <returns>The deserialised instance.</returns> </member> - <member name="T:NetSharp.Packets.Builtin.PingPacket"> + <member name="T:NetSharp.Deprecated.ComplexPacketHandler`2"> <summary> - A simple ping request packet for heartbeat monitoring and RTT measurement. + Represents a method that receives a request packet of the given type (<typeparamref name="TReq"/>) and + handles the request, returning a response packet of the given type (<typeparamref name="TRep"/>). </summary> + <typeparam name="TReq">The type of request packet handled by this delegate method.</typeparam> + <typeparam name="TRep">The type of response packet returned by this delegate method.</typeparam> + <param name="requestPacket">The request packet that should be handled by this delegate method.</param> + <param name="remoteEndPoint">The remote endpoint from which the request originated.</param> + <returns>The response packet to send back to the remote endpoint from which the request originated.</returns> </member> - <member name="M:NetSharp.Packets.Builtin.PingPacket.AfterDeserialisation"> - <inheritdoc /> + <member name="T:NetSharp.Deprecated.SimplePacketHandler`1"> + <summary> + Represents a method that receives a simple request packet of the given type (<typeparamref name="TReq"/>) and + handles the request, not returning any response packets. + </summary> + <typeparam name="TReq">The type of request packet handled by this delegate method.</typeparam> + <param name="requestPacket">The request packet that should be handled by this delegate method.</param> + <param name="remoteEndPoint">The remote endpoint from which the request originated.</param> </member> - <member name="M:NetSharp.Packets.Builtin.PingPacket.BeforeSerialisation"> - <inheritdoc /> + <member name="T:NetSharp.Deprecated.Server"> + <summary> + Provides methods for handling connected <see cref="T:NetSharp.Deprecated.IClient"/> instances. + </summary> </member> - <member name="M:NetSharp.Packets.Builtin.PingPacket.Deserialise(System.ReadOnlyMemory{System.Byte})"> - <inheritdoc /> + <member name="F:NetSharp.Deprecated.Server.complexPacketHandlers"> + <summary> + Maps a packet type id to the complex packet handler for that packet type. + </summary> </member> - <member name="M:NetSharp.Packets.Builtin.PingPacket.Serialise"> - <inheritdoc /> + <member name="F:NetSharp.Deprecated.Server.requestPacketDeserialisers"> + <summary> + Maps a packet type id to the raw packet deserialiser that deserialises raw packets to + <see cref="T:NetSharp.Deprecated.IRequestPacket"/> implementors. + </summary> </member> - <member name="T:NetSharp.Packets.Builtin.PingResponsePacket"> + <member name="F:NetSharp.Deprecated.Server.serverShutdownCancellationTokenSource"> <summary> - A response packet for the <see cref="T:NetSharp.Packets.Builtin.PingPacket"/>. + Cancellation token source to stop handling client sockets when the server should be shut down. </summary> </member> - <member name="P:NetSharp.Packets.Builtin.PingResponsePacket.RequestPacket"> - <inheritdoc /> + <member name="F:NetSharp.Deprecated.Server.simplePacketHandlers"> + <summary> + Maps a packet type id to the simple packet handler for that packet type. + </summary> </member> - <member name="M:NetSharp.Packets.Builtin.PingResponsePacket.AfterDeserialisation"> - <inheritdoc /> + <member name="M:NetSharp.Deprecated.Server.#ctor"> + <summary> + Initialises a new instance of the <see cref="T:NetSharp.Deprecated.Server"/> class. + </summary> </member> - <member name="M:NetSharp.Packets.Builtin.PingResponsePacket.BeforeSerialisation"> - <inheritdoc /> + <member name="M:NetSharp.Deprecated.Server.Finalize"> + <summary> + Destroys an instance of the <see cref="T:NetSharp.Deprecated.Server"/> class. + </summary> </member> - <member name="M:NetSharp.Packets.Builtin.PingResponsePacket.Deserialise(System.ReadOnlyMemory{System.Byte})"> - <inheritdoc /> + <member name="T:NetSharp.Deprecated.Server.RawRequestPacketDeserialiser"> + <summary> + Represents a method that receives a raw packet, and deserialises it into an <see cref="T:NetSharp.Deprecated.IRequestPacket"/> implementor. + </summary> + <param name="rawPacket">The raw packet that was received from the network.</param> + <returns>The deserialised instance of the packet.</returns> </member> - <member name="M:NetSharp.Packets.Builtin.PingResponsePacket.Serialise"> - <inheritdoc /> + <member name="M:NetSharp.Deprecated.Server.RegisterInternalPacketHandlers"> + <summary> + Registers packet handlers for every internal library packet. + </summary> </member> - <member name="T:NetSharp.Packets.Builtin.SimpleDataPacket"> + <member name="F:NetSharp.Deprecated.Server.PendingConnectionBacklog"> <summary> - A simple one-time-use data transfer packet, that allows for the transmission of an arbitrary number of frames. + The maximum number of connections that are allowed in the connection backlog. </summary> </member> - <member name="F:NetSharp.Packets.Builtin.SimpleDataPacket.RequestBuffer"> + <member name="F:NetSharp.Deprecated.Server.DefaultNetworkOperationTimeout"> <summary> - The data that should be transferred across the network. + The default timeout value for all network operations. </summary> </member> - <member name="M:NetSharp.Packets.Builtin.SimpleDataPacket.#ctor"> + <member name="F:NetSharp.Deprecated.Server.serverShutdownCancellationToken"> <summary> - Initialises a new instance of the <see cref="T:NetSharp.Packets.Builtin.SimpleDataPacket"/> class. + The cancellation token that will be set when the server must be shut down. </summary> </member> - <member name="M:NetSharp.Packets.Builtin.SimpleDataPacket.#ctor(System.Memory{System.Byte})"> + <member name="F:NetSharp.Deprecated.Server.socket"> <summary> - Initialises a new instance of the <see cref="T:NetSharp.Packets.Builtin.SimpleDataPacket"/> class. + The <see cref="T:System.Net.Sockets.Socket"/> underlying the connection. </summary> - <param name="buffer">The data that this request packet should contain.</param> </member> - <member name="M:NetSharp.Packets.Builtin.SimpleDataPacket.AfterDeserialisation"> - <inheritdoc /> + <member name="F:NetSharp.Deprecated.Server.socketOptions"> + <summary> + Backing field for the <see cref="P:NetSharp.Deprecated.Server.SocketOptions"/> property. + </summary> </member> - <member name="M:NetSharp.Packets.Builtin.SimpleDataPacket.BeforeSerialisation"> - <inheritdoc /> + <member name="F:NetSharp.Deprecated.Server.runServer"> + <summary> + Whether the server should be ran. + </summary> </member> - <member name="M:NetSharp.Packets.Builtin.SimpleDataPacket.Deserialise(System.ReadOnlyMemory{System.Byte})"> - <inheritdoc /> + <member name="M:NetSharp.Deprecated.Server.#ctor(System.Net.Sockets.SocketType,System.Net.Sockets.ProtocolType)"> + <summary> + Initialises a new instance of the <see cref="T:NetSharp.Deprecated.Server"/> class. + </summary> + <param name="socketType">The socket type for the underlying socket.</param> + <param name="protocolType">The protocol type for the underlying socket.</param> + <param name="socketManager">The <see cref="!:Utils.Socket_Options.SocketOptions"/> implementation to use.</param> </member> - <member name="M:NetSharp.Packets.Builtin.SimpleDataPacket.Serialise"> - <inheritdoc /> + <member name="M:NetSharp.Deprecated.Server.#ctor(System.Net.Sockets.SocketType,System.Net.Sockets.ProtocolType,System.TimeSpan)"> + <summary> + Initialises a new instance of the <see cref="T:NetSharp.Deprecated.Server"/> class. + </summary> + <param name="socketType">The socket type for the underlying socket.</param> + <param name="protocolType">The protocol type for the underlying socket.</param> + <param name="socketManager">The <see cref="!:Utils.Socket_Options.SocketOptions"/> manager to use.</param> + <param name="networkOperationTimeout">The timeout value for send and receive operations over the network.</param> </member> - <member name="T:NetSharp.Packets.NetworkErrorCode"> + <member name="M:NetSharp.Deprecated.Server.DeserialiseRequestPacket(System.UInt32,NetSharp.Deprecated.SerialisedPacket@)"> <summary> - Enumerates the possible error codes for network operations, being held in the packet. + Deserialises the given <see cref="T:NetSharp.Deprecated.NetworkPacket"/> struct into an <see cref="T:NetSharp.Deprecated.IRequestPacket"/> implementor. </summary> + <param name="packetType">The type id of packet that we should deserialise to.</param> + <param name="rawRequestPacket">The packet that should be deserialised.</param> + <returns>The deserialised packet instance, cast to the <see cref="T:NetSharp.Deprecated.IRequestPacket"/> interface.</returns> </member> - <member name="F:NetSharp.Packets.NetworkErrorCode.Ok"> + <member name="M:NetSharp.Deprecated.Server.Dispose(System.Boolean)"> <summary> - Signifies that there was no error during transmission. + Disposes of this <see cref="T:NetSharp.Deprecated.Server"/> instance. </summary> + <param name="disposing">Whether this instance is being disposed.</param> </member> - <member name="F:NetSharp.Packets.NetworkErrorCode.Error"> + <member name="M:NetSharp.Deprecated.Server.DoHandleClientAsync(System.Object)"> <summary> - A generic error occurred during packet transmission. + Provides a task that represents the handling of a client. Calls the abstract <see cref="M:NetSharp.Deprecated.Server.HandleClientAsync(NetSharp.Deprecated.Server.ClientHandlerArgs,System.Threading.CancellationToken)"/> method. </summary> + <param name="clientHandlerArgsObj">The object representing the passed <see cref="T:NetSharp.Deprecated.Server.ClientHandlerArgs"/> instance.</param> </member> - <member name="T:NetSharp.Packets.NetworkPacket"> + <member name="M:NetSharp.Deprecated.Server.HandleClientAsync(NetSharp.Deprecated.Server.ClientHandlerArgs,System.Threading.CancellationToken)"> <summary> - Represents a low-level packet that is transmitted over the network. + Handles a client asynchronously. </summary> + <param name="args">The client handler arguments that should be passed to the client handler.</param> + <param name="cancellationToken">Cancellation token set when the server is shutting down.</param> </member> - <member name="M:NetSharp.Packets.NetworkPacket.#ctor(System.ReadOnlyMemory{System.Byte},NetSharp.Packets.NetworkPacketHeader,NetSharp.Packets.NetworkPacketFooter)"> + <member name="M:NetSharp.Deprecated.Server.HandleRequestPacket(System.UInt32,NetSharp.Deprecated.IRequestPacket@,System.Net.EndPoint@)"> <summary> - Initialises a new instance of the <see cref="T:NetSharp.Packets.NetworkPacket"/> struct. + Handles the given request packet with a registered packet handler. In this case, a complex packet handler + will override any registered simple packet handlers. </summary> - <param name="data">The data that should be transmitted in the packet.</param> - <param name="header">The header for the packet.</param> - <param name="footer">The footer for the packet.</param> + <param name="packetType">The type id of the packet that we should handle.</param> + <param name="requestPacket">The packet instance that should be handled.</param> + <param name="remoteEndPoint">The remote endpoint from which the request packet originated.</param> + <returns>The response packet that should be sent back to the remote endpoint.</returns> </member> - <member name="F:NetSharp.Packets.NetworkPacket.DataSegmentSize"> + <member name="M:NetSharp.Deprecated.Server.OnClientConnected(System.Net.EndPoint)"> <summary> - The number of bytes allocated in each packet for user data. + Invokes the <see cref="E:NetSharp.Deprecated.Server.ClientConnected"/> event. </summary> + <param name="remoteEndPoint">The remote endpoint with which a connection was made.</param> </member> - <member name="F:NetSharp.Packets.NetworkPacket.FooterSize"> + <member name="M:NetSharp.Deprecated.Server.OnClientDisconnected(System.Net.EndPoint)"> <summary> - The number of bytes taken up in each packet by its footer. + Invokes the <see cref="E:NetSharp.Deprecated.Server.ClientDisconnected"/> event. </summary> + <param name="remoteEndPoint">The remote endpoint with which a connection was lost.</param> </member> - <member name="F:NetSharp.Packets.NetworkPacket.HeaderSize"> + <member name="M:NetSharp.Deprecated.Server.OnServerStarted"> <summary> - The number of bytes taken up in each packet by its header. + Invokes the <see cref="E:NetSharp.Deprecated.Server.ServerStarted"/> event. </summary> </member> - <member name="F:NetSharp.Packets.NetworkPacket.PacketSize"> + <member name="M:NetSharp.Deprecated.Server.OnServerStopped"> <summary> - The size of each packet, including its header, footer, and data segment. + Invokes the <see cref="E:NetSharp.Deprecated.Server.ServerStopped"/> event. </summary> </member> - <member name="F:NetSharp.Packets.NetworkPacket.DataBuffer"> + <member name="M:NetSharp.Deprecated.Server.TryBind(System.Net.EndPoint,System.TimeSpan)"> <summary> - The data held in this packet. + Attempts to synchronously bind the underlying socket to the given local endpoint. Blocks. + If the timeout is exceeded the binding attempt is aborted and the method returns false. </summary> + <param name="localEndPoint">The local endpoint to bind to.</param> + <param name="timeout">The timeout within which to attempt the binding.</param> + <returns>Whether the binding was successful or not.</returns> </member> - <member name="M:NetSharp.Packets.NetworkPacket.#ctor(System.ReadOnlyMemory{System.Byte},System.Int32,System.UInt32,NetSharp.Packets.NetworkErrorCode,System.Boolean)"> + <member name="M:NetSharp.Deprecated.Server.TryBindAsync(System.Net.EndPoint,System.TimeSpan)"> <summary> - Initialises a new instance of the <see cref="T:NetSharp.Packets.NetworkPacket"/> struct. + Attempts to asynchronously bind the underlying socket to the given local endpoint. Does not block. + If the timeout is exceeded the binding attempt is aborted and the method returns false. </summary> - <param name="data">The data that should be transmitted in the packet.</param> - <param name="dataLength">The number of bytes that are held in the given data buffer.</param> - <param name="type">The packet type.</param> - <param name="errorCode">The error code associated with this transmission.</param> - <param name="hasSucceedingPacket">Whether this packet has a succeeding packet in the packet chain.</param> + <param name="localEndPoint">The local endpoint to bind to.</param> + <param name="timeout">The timeout within which to attempt the binding.</param> + <returns>Whether the binding was successful or not.</returns> </member> - <member name="M:NetSharp.Packets.NetworkPacket.Deserialise(System.Memory{System.Byte})"> + <member name="T:NetSharp.Deprecated.Server.ClientHandlerArgs"> <summary> - Deserialises the given buffer into a packet instance. + Holds information about the arguments passed to every client handler task. </summary> - <param name="buffer">The byte buffer to serialise.</param> - <returns>The deserialised packet instance.</returns> </member> - <member name="M:NetSharp.Packets.NetworkPacket.Serialise(NetSharp.Packets.NetworkPacket)"> + <member name="M:NetSharp.Deprecated.Server.ClientHandlerArgs.#ctor(System.Net.EndPoint,System.Net.Sockets.Socket)"> <summary> - Serialises the given packet instance to a new byte buffer. + Initialises a new instance of the <see cref="T:NetSharp.Deprecated.Server.ClientHandlerArgs"/> struct. </summary> - <param name="instance">The packet instance to serialise.</param> - <returns>The byte buffer that represents the packet instance.</returns> + <param name="remoteEndPoint">The remote endpoint of the client that should be handled.</param> + <param name="handlerSocket">The handler socket of the client that should be handled.</param> </member> - <member name="M:NetSharp.Packets.NetworkPacket.SerialiseToBuffer(System.Memory{System.Byte},NetSharp.Packets.NetworkPacket)"> + <member name="F:NetSharp.Deprecated.Server.ClientHandlerArgs.ClientEndPoint"> <summary> - Serialises the given packet instance into the given byte buffer. + The remote endpoint for the client being handled. </summary> - <param name="buffer"> - The buffer to which the instance should be serialised. Must be at least of size <see cref="F:NetSharp.Packets.NetworkPacket.PacketSize"/>. - </param> - <param name="instance">The packet instance to serialise.</param> - <exception cref="T:System.ArgumentException">Thrown if the given buffer is too small.</exception> </member> - <member name="F:NetSharp.Packets.NetworkPacketFooter.Size"> + <member name="F:NetSharp.Deprecated.Server.ClientHandlerArgs.ClientSocket"> <summary> - The number of bytes taken up by a packet footer. + The client handler socket for the client being handled. Is only set if using TCP. </summary> </member> - <member name="F:NetSharp.Packets.NetworkPacketHeader.Size"> + <member name="M:NetSharp.Deprecated.Server.ClientHandlerArgs.ForTcpClientHandler(System.Net.Sockets.Socket@)"> <summary> - The number of bytes taken up by a packet header. + Constructs a new instance of the <see cref="T:NetSharp.Deprecated.Server.ClientHandlerArgs"/> for a TCP client. </summary> + <returns>A new instance of the <see cref="T:NetSharp.Deprecated.Server.ClientHandlerArgs"/>, setup for a TCP client.</returns> </member> - <member name="F:NetSharp.Packets.NetworkPacketHeader.DataLength"> + <member name="M:NetSharp.Deprecated.Server.ClientHandlerArgs.ForUdpClientHandler(System.Net.EndPoint@)"> <summary> - The number of bytes of data held in the packet. + Constructs a new instance of the <see cref="T:NetSharp.Deprecated.Server.ClientHandlerArgs"/> for a UDP client. </summary> + <returns>A new instance of the <see cref="T:NetSharp.Deprecated.Server.ClientHandlerArgs"/>, setup for a UDP client.</returns> </member> - <member name="F:NetSharp.Packets.NetworkPacketHeader.ErrorCode"> + <member name="E:NetSharp.Deprecated.Server.ClientConnected"> <summary> - The error code for this packet. + Signifies that a connection with a remote endpoint has been made. </summary> </member> - <member name="F:NetSharp.Packets.NetworkPacketHeader.Type"> + <member name="E:NetSharp.Deprecated.Server.ClientDisconnected"> <summary> - The packet type. + Signifies that a connection with a remote endpoint has been lost. </summary> </member> - <member name="T:NetSharp.Packets.PacketRegistry"> + <member name="E:NetSharp.Deprecated.Server.ServerStarted"> <summary> - Provides method of registering request packets and their relevant response packets, as well as mapping their ids. + Signifies that the server was started and clients will start being accepted. </summary> </member> - <member name="F:NetSharp.Packets.PacketRegistry.AutomaticPacketTypeIdStartPoint"> + <member name="E:NetSharp.Deprecated.Server.ServerStopped"> <summary> - The start id for automatically generated packet type ids. Any custom packet type ids lower than this value - that come from external assemblies will be incremented by this value, to ensure that there are no clashes. + Signifies that the server was stopped and clients will stop being accepted. </summary> </member> - <member name="F:NetSharp.Packets.PacketRegistry.currentAutomaticPacketTypeIdCounterLockObject"> + <member name="P:NetSharp.Deprecated.Server.NetworkOperationTimeout"> <summary> - The lock object for synchronising access to the <see cref="F:NetSharp.Packets.PacketRegistry.currentAutomaticPacketTypeIdCounter"/> field. + The timeout value for network operations such as sending bytes or receiving bytes over the network. </summary> </member> - <member name="F:NetSharp.Packets.PacketRegistry.idToPacketTypeMap"> + <member name="P:NetSharp.Deprecated.Server.SocketOptions"> <summary> - Maps a packet type id to its relevant packet type, and vice-versa. + The configured socket options for the underlying connection. </summary> </member> - <member name="F:NetSharp.Packets.PacketRegistry.LibraryAssembly"> + <member name="M:NetSharp.Deprecated.Server.RunAsync(System.Net.EndPoint)"> + <inheritdoc /> + </member> + <member name="M:NetSharp.Deprecated.Server.Shutdown"> + <inheritdoc /> + </member> + <member name="M:NetSharp.Deprecated.Server.TryDeregisterComplexPacketHandler``2(NetSharp.Deprecated.ComplexPacketHandler{``0,``1}@)"> + <inheritdoc /> + </member> + <member name="M:NetSharp.Deprecated.Server.TryDeregisterSimplePacketHandler``1(NetSharp.Deprecated.SimplePacketHandler{``0}@)"> + <inheritdoc /> + </member> + <member name="M:NetSharp.Deprecated.Server.TryRegisterComplexPacketHandler``2(NetSharp.Deprecated.ComplexPacketHandler{``0,``1})"> + <inheritdoc /> + </member> + <member name="M:NetSharp.Deprecated.Server.TryRegisterSimplePacketHandler``1(NetSharp.Deprecated.SimplePacketHandler{``0})"> + <inheritdoc /> + </member> + <member name="T:NetSharp.Deprecated.ServerClientConnection"> <summary> - The assembly that represents the library, where all of the builtin packets are defined. + Base class for connections, holding methods shared between the <see cref="T:NetSharp.Deprecated.Client"/> and <see cref="T:NetSharp.Deprecated.Server"/> classes. </summary> </member> - <member name="F:NetSharp.Packets.PacketRegistry.requestToResponseMap"> + <member name="F:NetSharp.Deprecated.ServerClientConnection.logger"> <summary> - Maps a request packet to its relevant response packet, and vice-versa. + The logger to which the server can log messages. </summary> </member> - <member name="F:NetSharp.Packets.PacketRegistry.currentAutomaticPacketTypeIdCounter"> + <member name="M:NetSharp.Deprecated.ServerClientConnection.#ctor"> <summary> - The current id for registered packets. + Initialises a new instance of the <see cref="T:NetSharp.Deprecated.ServerClientConnection"/> class. </summary> </member> - <member name="M:NetSharp.Packets.PacketRegistry.GetNewPacketTypeId(System.Type)"> + <member name="M:NetSharp.Deprecated.ServerClientConnection.Dispose(System.Boolean)"> <summary> - Fetches the packet type id of the given packet type. If the packet type is declared outside of the library - assembly, then its value is incremented by the <see cref="F:NetSharp.Packets.PacketRegistry.AutomaticPacketTypeIdStartPoint"/> value. This ensure that - there are no clashes between the packet type ids of packets declared in the library and external packets. + Disposes of this <see cref="T:NetSharp.Deprecated.ServerClientConnection"/> instance. </summary> - <param name="packetType">The packet type whose id should be fetched.</param> - <returns>The id of the given packet type.</returns> + <param name="disposing">Whether this instance is being disposed.</param> </member> - <member name="M:NetSharp.Packets.PacketRegistry.DeregisterPacketType(System.Type,System.Type)"> + <member name="M:NetSharp.Deprecated.ServerClientConnection.OnBytesReceived(System.Net.EndPoint,System.Int32)"> <summary> - Deregisters the given packet type from the registry. + Invokes the <see cref="E:NetSharp.Deprecated.ServerClientConnection.BytesReceived"/> event. </summary> - <param name="requestPacketType">The request packet type to deregister, if it is registered.</param> - <param name="responsePacketType">The response packet associated with the request packet.</param> + <param name="remoteEndPoint">The remote endpoint from which the bytes were received.</param> + <param name="bytesReceived">The number of bytes that were received from the remote endpoint.</param> </member> - <member name="M:NetSharp.Packets.PacketRegistry.DeregisterPacketTypes(System.Collections.Generic.Dictionary{System.Type,System.Type})"> + <member name="M:NetSharp.Deprecated.ServerClientConnection.OnBytesSent(System.Net.EndPoint,System.Int32)"> <summary> - Deregisters the given packet types from the registry. + Invokes the <see cref="E:NetSharp.Deprecated.ServerClientConnection.BytesSent"/> event. </summary> - <param name="requestToResponsePacketTypeMap">The list of packet types to deregister, if they are registered.</param> + <param name="remoteEndPoint">The remote endpoint to which the bytes were sent.</param> + <param name="bytesSent">The number of bytes that were sent to the remote endpoint.</param> </member> - <member name="M:NetSharp.Packets.PacketRegistry.GetPacketId(System.Type)"> + <member name="E:NetSharp.Deprecated.ServerClientConnection.BytesReceived"> <summary> - Returns the packet type id associated with the given packet type. + Signifies that some data has been received from the remote endpoint. </summary> - <param name="packetType">The packet type whose id to fetch.</param> - <returns>The id of the packet type given.</returns> </member> - <member name="M:NetSharp.Packets.PacketRegistry.GetPacketId``1"> + <member name="E:NetSharp.Deprecated.ServerClientConnection.BytesSent"> <summary> - Returns the packet type id associated with the given packet type. + Signifies that some data was sent to the remote endpoint. </summary> - <typeparam name="TPacket">The packet type whose id to fetch.</typeparam> - <returns>The id of the packet type given.</returns> </member> - <member name="M:NetSharp.Packets.PacketRegistry.GetPacketType(System.UInt32)"> + <member name="M:NetSharp.Deprecated.ServerClientConnection.ChangeLoggingStream(System.IO.Stream,NetSharp.Deprecated.LogLevel)"> <summary> - Returns the packet type associated with the given id. + Makes the client log to the given stream. </summary> - <param name="packetTypeId">The packet id whose mapped type to fetch.</param> - <returns>The packet type mapped by the given id.</returns> + <param name="loggingStream">The stream that new messages should be logged to.</param> + <param name="minimumMessageSeverityLevel"> + The minimum severity level that new messages must have to be logged to the stream. + </param> + </member> + <member name="M:NetSharp.Deprecated.ServerClientConnection.Dispose"> + <inheritdoc /> </member> - <member name="M:NetSharp.Packets.PacketRegistry.GetRequestPacketType``1"> + <member name="T:NetSharp.Deprecated.ServerExtensions"> <summary> - Returns the type of request packet mapped by the given response packet type. + Provides additional methods and functionality to the <see cref="T:NetSharp.Deprecated.Server"/> class. </summary> - <typeparam name="TResponse">The response packet type whose request packet type to fetch.</typeparam> - <returns>The request packet type, <c>null</c> if no type is mapped.</returns> </member> - <member name="M:NetSharp.Packets.PacketRegistry.GetRequestPacketType(System.Type)"> + <member name="M:NetSharp.Deprecated.ServerExtensions.Run(NetSharp.Deprecated.Server,System.Net.IPAddress,System.Int32)"> <summary> - Returns the type of request packet mapped by the given response packet type. + Starts the server synchronously and starts accepting client connections. Blocks. </summary> - <param name="responsePacketType">The response packet type whose request packet type to fetch.</param> - <returns>The request packet type, <c>null</c> if no type is mapped.</returns> + <param name="instance">The instance on which this extension method should be called.</param> + <param name="localAddress">The local IP address to bind to.</param> + <param name="localPort">The local port to bind to.</param> </member> - <member name="M:NetSharp.Packets.PacketRegistry.GetResponsePacketType``1"> + <member name="M:NetSharp.Deprecated.ServerExtensions.Run(NetSharp.Deprecated.Server,System.Net.IPAddress)"> <summary> - Returns the type of response packet mapped by the given request packet type. + Starts the server synchronously and starts accepting client connections. Blocks. Uses the default connection port. </summary> - <typeparam name="TRequest">The request packet type whose response packet type to fetch.</typeparam> - <returns>The response packet type, <c>null</c> if no type is mapped.</returns> + <param name="instance">The instance on which this extension method should be called.</param> + <param name="localAddress">The local IP address to bind to.</param> </member> - <member name="M:NetSharp.Packets.PacketRegistry.GetResponsePacketType(System.Type)"> + <member name="M:NetSharp.Deprecated.ServerExtensions.RunAsync(NetSharp.Deprecated.Server,System.Net.IPAddress)"> <summary> - Returns the type of response packet mapped by the given request packet type. + Starts the server asynchronously and starts accepting client connections. Does not block. Uses the default + connection port. </summary> - <param name="requestPacketType">The request packet type whose response packet type to fetch.</param> - <returns>The response packet type, <c>null</c> if no type is mapped.</returns> + <param name="instance">The instance on which this extension method should be called.</param> + <param name="localAddress">The local IP address to bind to.</param> </member> - <member name="M:NetSharp.Packets.PacketRegistry.RegisterPacketSourceAssemblies(System.Reflection.Assembly[])"> + <member name="M:NetSharp.Deprecated.ServerExtensions.RunAsync(NetSharp.Deprecated.Server,System.Net.IPAddress,System.Int32)"> <summary> - Rebuilds the packet registry, by registering every <see cref="T:NetSharp.Deprecated.IPacket"/> inheritor in the given assemblies. + Starts the server asynchronously and starts accepting client connections. Does not block. </summary> - <param name="packetSourceAssemblies"> - The assemblies from which the packet types to register are sourced. - </param> + <param name="instance">The instance on which this extension method should be called.</param> + <param name="localAddress">The local IP address to bind to.</param> + <param name="localPort">The local port to bind to.</param> </member> - <member name="M:NetSharp.Packets.PacketRegistry.RegisterPacketSourceAssembly(System.Reflection.Assembly)"> + <member name="T:NetSharp.Deprecated.SocketAcceptor"> <summary> - Registers all the <see cref="T:NetSharp.Deprecated.IPacket"/> implementors in the given assembly. + Helper class providing awaitable wrappers around asynchronous Accept, Connect, and Disconnect operations. </summary> - <param name="packetSourceAssembly">The assembly whose packet types to register.</param> </member> - <member name="M:NetSharp.Packets.PacketRegistry.RegisterPacketType(System.Type,System.Type)"> + <member name="M:NetSharp.Deprecated.SocketAcceptor.AcceptAsync(System.Net.Sockets.Socket,System.Threading.CancellationToken)"> <summary> - Registers the given packet type to the registry. + Provides an awaitable wrapper around an asynchronous socket accept operation. </summary> - <param name="requestPacketType">The request packet type to register, if it is not registered.</param> - <param name="responsePacketType">The response packet associated with the request packet.</param> + <param name="socket">The socket which should be used to accept an incoming connection attempt.</param> + <param name="cancellationToken">The cancellation token to observe for the operation.</param> + <returns>The accepted socket.</returns> </member> - <member name="M:NetSharp.Packets.PacketRegistry.RegisterPacketTypes(System.Collections.Generic.Dictionary{System.Type,System.Type})"> + <member name="M:NetSharp.Deprecated.SocketAcceptor.ConnectAsync(System.Net.Sockets.Socket,System.Net.EndPoint,System.Threading.CancellationToken)"> <summary> - Registers the given packet types to the registry. + Provides an awaitable wrapper around an asynchronous socket connect operation. </summary> - <param name="requestToResponsePacketTypeMap"> - The dictionary mapping the request packet types to register, to their relevant response packet types. - The response packet type can be null; then the request packet type is treated as a 'simple' packet. - </param> + <param name="socket">The socket which should asynchronously connect to the remote endpoint.</param> + <param name="remoteEndPoint">The remote endpoint to which the socket should connect.</param> + <param name="cancellationToken">The cancellation token to observe for the operation.</param> </member> - <member name="M:NetSharp.Packets.PacketRegistry.#cctor"> + <member name="M:NetSharp.Deprecated.SocketAcceptor.DisconnectAsync(System.Net.Sockets.Socket,System.Threading.CancellationToken)"> <summary> - Initialises a new instance of the <see cref="T:NetSharp.Packets.PacketRegistry"/> class. + Provides an awaitable wrapper around an asynchronous socket disconnect operation. </summary> + <param name="socket">The socket which should asynchronously disconnect from its remote endpoint.</param> + <param name="cancellationToken">The cancellation token to observe for the operation.</param> </member> - <member name="T:NetSharp.Packets.PacketTypeIdAttribute"> + <member name="M:NetSharp.Deprecated.SocketClient.Finalize"> <summary> - Allows the placing of a custom packet type on a class or struct. This is used if the class or struct - inherits from <see cref="T:NetSharp.Deprecated.IRequestPacket"/> or <see cref="T:NetSharp.Deprecated.IResponsePacket`1"/>. + Destroys a socket client instance. </summary> </member> - <member name="M:NetSharp.Packets.PacketTypeIdAttribute.#ctor(System.UInt32)"> + <member name="M:NetSharp.Deprecated.SocketClient.Dispose(System.Boolean)"> <summary> - Initialises a new instance of the <see cref="T:NetSharp.Packets.PacketTypeIdAttribute"/> attribute. + Implementation of dispose pattern. </summary> - <param name="type">The custom type id that the decorated packet type should have.</param> + <param name="disposing"> + Whether this method is being called by the object finalizer, or by the <see cref="M:NetSharp.Deprecated.SocketClient.Dispose"/> method. + </param> </member> - <member name="P:NetSharp.Packets.PacketTypeIdAttribute.Id"> + <member name="M:NetSharp.Deprecated.SocketClient.Dispose"> + <inheritdoc /> + </member> + <member name="T:NetSharp.Deprecated.SocketOperations"> <summary> - The custom type id that the decorated packet type should have. This overrides the automatically generated id. + Provides helper awaitable functions for wrapping the <see cref="T:System.Net.Sockets.SocketAsyncEventArgs"/> pattern. </summary> </member> - <member name="T:NetSharp.Pipelines.PacketPipeline`3"> + <member name="T:NetSharp.Deprecated.SocketOptions"> <summary> - Represents a pipeline of transformations that packets must undergo. + Allows for manipulation of socket options. </summary> - <typeparam name="TInput">The type of packet the pipeline receives.</typeparam> - <typeparam name="TIntermediate">The type of packet the pipeline internally handles.</typeparam> - <typeparam name="TOutput">The type of packet the pipeline outputs.</typeparam> </member> - <member name="M:NetSharp.Pipelines.PacketPipeline`3.ProcessPacket(`0)"> + <member name="F:NetSharp.Deprecated.SocketOptions.managedSocket"> <summary> - Passes the given packet through the pipeline. + The <see cref="T:System.Net.Sockets.Socket"/> instance whose settings are being managed. </summary> - <param name="inputPacket">The incoming packet.</param> - <returns>The outgoing transformed packet.</returns> </member> - <member name="T:NetSharp.Pipelines.PacketPipelineStage`2"> + <member name="M:NetSharp.Deprecated.SocketOptions.#ctor(System.Net.Sockets.Socket@)"> <summary> - Represents a single transformation applied to a packet traveling through the pipeline. + Initialises a new instance of the <see cref="T:NetSharp.Deprecated.SocketOptions"/> class. </summary> - <typeparam name="TInput">The type the transformation takes as input.</typeparam> - <typeparam name="TOutput">The type the transformation produces as output.</typeparam> + <param name="socket">The <see cref="T:System.Net.Sockets.Socket"/> instance whose options should be managed.</param> </member> - <member name="T:NetSharp.Pipelines.PacketPipelineBuilder`3"> + <member name="P:NetSharp.Deprecated.SocketOptions.DualMode"> <summary> - Allows for configuring and subsequently building a <see cref="T:NetSharp.Pipelines.PacketPipeline`3"/> instance. + Whether this <see cref="T:System.Net.Sockets.Socket"/> can operate in dual IPv4 / IPv6 mode. </summary> - <typeparam name="TInput">The type of packet that will be submitted to the pipeline.</typeparam> - <typeparam name="TIntermediate">The type of packet that will be handled internally by the pipeline.</typeparam> - <typeparam name="TOutput">The type of packet that will be requested from the pipeline.</typeparam> </member> - <member name="M:NetSharp.Pipelines.PacketPipelineBuilder`3.Build"> + <member name="P:NetSharp.Deprecated.SocketOptions.ForceFlush"> <summary> - Returns the currently configured <see cref="T:NetSharp.Pipelines.PacketPipeline`3"/> instance. + Whether sending a packet flushes underlying <see cref="T:System.Net.Sockets.NetworkStream"/>. </summary> - <returns>The configured <see cref="T:NetSharp.Pipelines.PacketPipeline`3"/> instance.</returns> - <exception cref="T:System.ArgumentNullException"> - Thrown when either <see cref="M:NetSharp.Pipelines.PacketPipelineBuilder`3.WithInputStage(System.Func{`0,`1}@)"/> or <see cref="M:NetSharp.Pipelines.PacketPipelineBuilder`3.WithOutputStage(System.Func{`1,`2}@)"/> have not been called. - </exception> + <remarks> + This value is only used in a <see cref="T:System.Net.Sockets.TcpClient"/> instance, which uses a <see cref="T:System.Net.Sockets.NetworkStream"/> + to send and receive data. A <see cref="T:System.Net.Sockets.UdpClient"/> is unaffected by this value. + </remarks> </member> - <member name="M:NetSharp.Pipelines.PacketPipelineBuilder`3.WithInputStage(System.Func{`0,`1}@)"> + <member name="P:NetSharp.Deprecated.SocketOptions.Fragment"> <summary> - Configures the input stage for the pipeline. + Whether this <see cref="T:System.Net.Sockets.Socket"/> is allowed to fragment frames that are too large to send in one go. </summary> - <param name="stage"> - The transformation that should be applied to incoming packets, to convert them from the <typeparamref name="TInput"/> - type to the <typeparamref name="TIntermediate"/> type that the pipeline handles internally. - </param> - <returns>The builder instance for further configuration.</returns> </member> - <member name="M:NetSharp.Pipelines.PacketPipelineBuilder`3.WithIntermediateStage(System.Func{`1,`1}@)"> + <member name="P:NetSharp.Deprecated.SocketOptions.HopLimit"> <summary> - Adds the given intermediate stage to the pipeline. + The hop limit for packets sent by this <see cref="T:System.Net.Sockets.Socket"/>. Comparable to IPv4s TTL (Time To Live). </summary> - <param name="stage"> - The transformation that should be applied to packets traveling through the pipeline. - </param> - <returns>The builder instance for further configuration.</returns> </member> - <member name="M:NetSharp.Pipelines.PacketPipelineBuilder`3.WithOutputStage(System.Func{`1,`2}@)"> + <member name="P:NetSharp.Deprecated.SocketOptions.IsChecksumEnabled"> <summary> - Configures the output stage for the pipeline. + Whether a checksum should be created for each UDP packet sent. </summary> - <param name="stage"> - The transformation that should be applied to outgoing packets, to convert them from the - <typeparamref name="TIntermediate"/> type used internally to the <typeparamref name="TOutput"/> type. - </param> - <returns>The builder instance for further configuration.</returns> </member> - <member name="T:NetSharp.Sockets.SocketAcceptor"> + <member name="P:NetSharp.Deprecated.SocketOptions.IsRoutingEnabled"> <summary> - Helper class providing awaitable wrappers around asynchronous Accept, Connect, and Disconnect operations. + Whether the packet should be sent directly to its destination or allowed to be routed through multiple destinations + first. </summary> </member> - <member name="M:NetSharp.Sockets.SocketAcceptor.AcceptAsync(System.Net.Sockets.Socket,System.Threading.CancellationToken)"> + <member name="P:NetSharp.Deprecated.SocketOptions.LocalEndPoint"> <summary> - Provides an awaitable wrapper around an asynchronous socket accept operation. + The local <see cref="T:System.Net.EndPoint"/> for the <see cref="F:NetSharp.Deprecated.SocketOptions.managedSocket"/>. </summary> - <param name="socket">The socket which should be used to accept an incoming connection attempt.</param> - <param name="cancellationToken">The cancellation token to observe for the operation.</param> - <returns>The accepted socket.</returns> </member> - <member name="M:NetSharp.Sockets.SocketAcceptor.ConnectAsync(System.Net.Sockets.Socket,System.Net.EndPoint,System.Threading.CancellationToken)"> + <member name="P:NetSharp.Deprecated.SocketOptions.LocalIPEndPoint"> <summary> - Provides an awaitable wrapper around an asynchronous socket connect operation. + The local <see cref="T:System.Net.IPEndPoint"/> for this <see cref="T:System.Net.Sockets.Socket"/> instance. </summary> - <param name="socket">The socket which should asynchronously connect to the remote endpoint.</param> - <param name="remoteEndPoint">The remote endpoint to which the socket should connect.</param> - <param name="cancellationToken">The cancellation token to observe for the operation.</param> </member> - <member name="M:NetSharp.Sockets.SocketAcceptor.DisconnectAsync(System.Net.Sockets.Socket,System.Threading.CancellationToken)"> + <member name="P:NetSharp.Deprecated.SocketOptions.RemoteEndPoint"> <summary> - Provides an awaitable wrapper around an asynchronous socket disconnect operation. + The remote <see cref="T:System.Net.EndPoint"/> for the <see cref="F:NetSharp.Deprecated.SocketOptions.managedSocket"/>. </summary> - <param name="socket">The socket which should asynchronously disconnect from its remote endpoint.</param> - <param name="cancellationToken">The cancellation token to observe for the operation.</param> </member> - <member name="M:NetSharp.Sockets.SocketClient.Finalize"> + <member name="P:NetSharp.Deprecated.SocketOptions.RemoteIPEndPoint"> <summary> - Destroys a socket client instance. + The remote <see cref="T:System.Net.IPEndPoint"/> that this <see cref="T:System.Net.Sockets.Socket"/> instance communicates with. </summary> </member> - <member name="M:NetSharp.Sockets.SocketClient.Dispose(System.Boolean)"> + <member name="P:NetSharp.Deprecated.SocketOptions.Ttl"> <summary> - Implementation of dispose pattern. + The 'Time To Live' for this <see cref="T:System.Net.Sockets.Socket"/>. </summary> - <param name="disposing"> - Whether this method is being called by the object finalizer, or by the <see cref="M:NetSharp.Sockets.SocketClient.Dispose"/> method. - </param> - </member> - <member name="M:NetSharp.Sockets.SocketClient.Dispose"> - <inheritdoc /> </member> - <member name="T:NetSharp.Sockets.SocketOperations"> + <member name="P:NetSharp.Deprecated.SocketOptions.UseLoopback"> <summary> - Provides helper awaitable functions for wrapping the <see cref="T:System.Net.Sockets.SocketAsyncEventArgs"/> pattern. + Whether this <see cref="T:System.Net.Sockets.Socket"/> should use a loopback address and bypass hardware. </summary> </member> - <member name="T:NetSharp.Sockets.SocketReader"> + <member name="T:NetSharp.Deprecated.SocketReader"> <summary> Helper class providing awaitable wrappers around asynchronous Receive and ReceiveFrom operations. </summary> </member> - <member name="M:NetSharp.Sockets.SocketReader.ReceiveFromAsync(System.Net.Sockets.Socket,System.Net.EndPoint,System.Net.Sockets.SocketFlags,System.Memory{System.Byte},System.Threading.CancellationToken)"> + <member name="M:NetSharp.Deprecated.SocketReader.ReceiveFromAsync(System.Net.Sockets.Socket,System.Net.EndPoint,System.Net.Sockets.SocketFlags,System.Memory{System.Byte},System.Threading.CancellationToken)"> <summary> Provides an awaitable wrapper around an asynchronous socket receive operation. </summary> @@ -2079,33 +1982,33 @@ <param name="cancellationToken">The cancellation token to observe for the operation.</param> <returns>The result of the receive operation.</returns> </member> - <member name="M:NetSharp.Sockets.SocketServer.Finalize"> + <member name="M:NetSharp.Deprecated.SocketServer.Finalize"> <summary> Destroys a socket server instance. </summary> </member> - <member name="F:NetSharp.Sockets.SocketServer.listenerSocket"> + <member name="F:NetSharp.Deprecated.SocketServer.listenerSocket"> <summary> The socket which should be used to listen for incoming data and to send outgoing data. </summary> </member> - <member name="M:NetSharp.Sockets.SocketServer.Dispose(System.Boolean)"> + <member name="M:NetSharp.Deprecated.SocketServer.Dispose(System.Boolean)"> <summary> Implementation of dispose pattern. </summary> <param name="disposing"> - Whether this method is being called by the object finalizer, or by the <see cref="M:NetSharp.Sockets.SocketServer.Dispose"/> method. + Whether this method is being called by the object finalizer, or by the <see cref="M:NetSharp.Deprecated.SocketServer.Dispose"/> method. </param> </member> - <member name="M:NetSharp.Sockets.SocketServer.Dispose"> + <member name="M:NetSharp.Deprecated.SocketServer.Dispose"> <inheritdoc /> </member> - <member name="T:NetSharp.Sockets.SocketWriter"> + <member name="T:NetSharp.Deprecated.SocketWriter"> <summary> Helper class providing awaitable wrappers around asynchronous Send and SendTo operations. </summary> </member> - <member name="M:NetSharp.Sockets.SocketWriter.SendToAsync(System.Net.Sockets.Socket,System.Net.EndPoint,System.Net.Sockets.SocketFlags,System.Memory{System.Byte},System.Threading.CancellationToken)"> + <member name="M:NetSharp.Deprecated.SocketWriter.SendToAsync(System.Net.Sockets.Socket,System.Net.EndPoint,System.Net.Sockets.SocketFlags,System.Memory{System.Byte},System.Threading.CancellationToken)"> <summary> Provides an awaitable wrapper around an asynchronous socket send operation. </summary> @@ -2116,62 +2019,180 @@ <param name="cancellationToken">The cancellation token to observe for the operation.</param> <returns>The number of bytes of data which were written to the remote endpoint.</returns> </member> - <member name="T:NetSharp.Utils.BiDictionary`2"> + <member name="T:NetSharp.Deprecated.TcpClient"> + <summary> + Provides methods for TCP communication with a connected <see cref="T:NetSharp.Deprecated.TcpServer"/> instance. + </summary> + </member> + <member name="M:NetSharp.Deprecated.TcpClient.#ctor"> + <inheritdoc /> + </member> + <member name="M:NetSharp.Deprecated.TcpClient.SendBytesAsync(System.Byte[],System.TimeSpan)"> + <inheritdoc /> + </member> + <member name="M:NetSharp.Deprecated.TcpClient.SendBytesWithResponseAsync(System.Byte[],System.TimeSpan)"> + <inheritdoc /> + </member> + <member name="M:NetSharp.Deprecated.TcpClient.SendComplexAsync``2(``0,System.TimeSpan)"> + <inheritdoc /> + </member> + <member name="M:NetSharp.Deprecated.TcpClient.SendSimpleAsync``1(``0,System.TimeSpan)"> + <inheritdoc /> + </member> + <member name="T:NetSharp.Deprecated.TcpServer"> + <summary> + Provides methods for TCP communication with connected <see cref="T:NetSharp.Deprecated.TcpClient"/> instances. + </summary> + </member> + <member name="M:NetSharp.Deprecated.TcpServer.HandleClientAsync(NetSharp.Deprecated.Server.ClientHandlerArgs,System.Threading.CancellationToken)"> + <inheritdoc /> + </member> + <member name="M:NetSharp.Deprecated.TcpServer.#ctor(System.TimeSpan)"> + <inheritdoc /> + </member> + <member name="M:NetSharp.Deprecated.TcpServer.#ctor"> + <inheritdoc /> + </member> + <member name="M:NetSharp.Deprecated.TcpServer.RunAsync(System.Net.EndPoint)"> + <inheritdoc /> + </member> + <member name="T:NetSharp.Deprecated.TcpSocketOptions"> + <summary> + Allows for manipulation of TCP socket options. + </summary> + </member> + <member name="M:NetSharp.Deprecated.TcpSocketOptions.#ctor(System.Net.Sockets.Socket@)"> + <inheritdoc /> + </member> + <member name="P:NetSharp.Deprecated.TcpSocketOptions.HopLimit"> + <inheritdoc /> + </member> + <member name="P:NetSharp.Deprecated.TcpSocketOptions.IsRoutingEnabled"> + <inheritdoc /> + </member> + <member name="P:NetSharp.Deprecated.TcpSocketOptions.UseLoopback"> + <inheritdoc /> + </member> + <member name="T:NetSharp.Deprecated.UdpClient"> + <summary> + Provides methods for UDP communication with a connected <see cref="T:NetSharp.Deprecated.UdpServer"/> instance. + </summary> + </member> + <member name="M:NetSharp.Deprecated.UdpClient.#ctor"> + <inheritdoc /> + </member> + <member name="M:NetSharp.Deprecated.UdpClient.SendBytesAsync(System.Byte[],System.TimeSpan)"> + <inheritdoc /> + </member> + <member name="M:NetSharp.Deprecated.UdpClient.SendBytesWithResponseAsync(System.Byte[],System.TimeSpan)"> + <inheritdoc /> + </member> + <member name="M:NetSharp.Deprecated.UdpClient.SendComplexAsync``2(``0,System.TimeSpan)"> + <inheritdoc /> + </member> + <member name="M:NetSharp.Deprecated.UdpClient.SendSimpleAsync``1(``0,System.TimeSpan)"> + <inheritdoc /> + </member> + <member name="T:NetSharp.Deprecated.UdpServer"> + <summary> + Provides methods for UDP communication with connected <see cref="T:NetSharp.Deprecated.UdpClient"/> instances. + </summary> + </member> + <member name="F:NetSharp.Deprecated.UdpServer.clientChannelOptions"> + <summary> + The options that should be applied to every channel created to handle a client. + </summary> + </member> + <member name="F:NetSharp.Deprecated.UdpServer.activeClients"> + <summary> + Holds currently connected and active clients, as well as their current received packet queues. + </summary> + </member> + <member name="M:NetSharp.Deprecated.UdpServer.HandleClientAsync(NetSharp.Deprecated.Server.ClientHandlerArgs,System.Threading.CancellationToken)"> + <inheritdoc /> + </member> + <member name="M:NetSharp.Deprecated.UdpServer.#ctor(System.TimeSpan)"> + <inheritdoc /> + </member> + <member name="M:NetSharp.Deprecated.UdpServer.#ctor"> + <inheritdoc /> + </member> + <member name="M:NetSharp.Deprecated.UdpServer.RunAsync(System.Net.EndPoint)"> + <inheritdoc /> + </member> + <member name="T:NetSharp.Deprecated.UdpSocketOptions"> + <summary> + Allows for manipulation of UDP socket options. + </summary> + </member> + <member name="M:NetSharp.Deprecated.UdpSocketOptions.#ctor(System.Net.Sockets.Socket@)"> + <inheritdoc /> + </member> + <member name="P:NetSharp.Deprecated.UdpSocketOptions.HopLimit"> + <inheritdoc /> + </member> + <member name="P:NetSharp.Deprecated.UdpSocketOptions.IsRoutingEnabled"> + <inheritdoc /> + </member> + <member name="P:NetSharp.Deprecated.UdpSocketOptions.UseLoopback"> + <inheritdoc /> + </member> + <member name="T:NetSharp.Deprecated.BiDictionary`2"> <summary> Represents a concurrent two-way dictionary, that can be indexed by either a key or a value. </summary> <typeparam name="K">The type of key that will be stored.</typeparam> <typeparam name="V">The type of value that will be stored.</typeparam> </member> - <member name="F:NetSharp.Utils.BiDictionary`2.keyToValueMap"> + <member name="F:NetSharp.Deprecated.BiDictionary`2.keyToValueMap"> <summary> Maps keys to their corresponding values. </summary> </member> - <member name="F:NetSharp.Utils.BiDictionary`2.valueToKeyMap"> + <member name="F:NetSharp.Deprecated.BiDictionary`2.valueToKeyMap"> <summary> Maps values to their corresponding keys. </summary> </member> - <member name="M:NetSharp.Utils.BiDictionary`2.#ctor"> + <member name="M:NetSharp.Deprecated.BiDictionary`2.#ctor"> <summary> - Initialises a new instance of the <see cref="T:NetSharp.Utils.BiDictionary`2"/> class. + Initialises a new instance of the <see cref="T:NetSharp.Deprecated.BiDictionary`2"/> class. </summary> </member> - <member name="P:NetSharp.Utils.BiDictionary`2.Item(`1)"> + <member name="P:NetSharp.Deprecated.BiDictionary`2.Item(`1)"> <summary> Indexes this instance with the given value. </summary> <param name="index">The value whose key to get or set.</param> <returns>The fetched key.</returns> </member> - <member name="P:NetSharp.Utils.BiDictionary`2.Item(`0)"> + <member name="P:NetSharp.Deprecated.BiDictionary`2.Item(`0)"> <summary> Indexes this instance with the given key. </summary> <param name="index">The key whose value to get or set.</param> <returns>The fetched value.</returns> </member> - <member name="M:NetSharp.Utils.BiDictionary`2.Clear"> + <member name="M:NetSharp.Deprecated.BiDictionary`2.Clear"> <summary> - Clears this instance's <see cref="F:NetSharp.Utils.BiDictionary`2.keyToValueMap"/> and <see cref="F:NetSharp.Utils.BiDictionary`2.valueToKeyMap"/>. + Clears this instance's <see cref="F:NetSharp.Deprecated.BiDictionary`2.keyToValueMap"/> and <see cref="F:NetSharp.Deprecated.BiDictionary`2.valueToKeyMap"/>. </summary> </member> - <member name="M:NetSharp.Utils.BiDictionary`2.ContainsKey(`0@)"> + <member name="M:NetSharp.Deprecated.BiDictionary`2.ContainsKey(`0@)"> <summary> Whether this instance contains the given key. </summary> <param name="key">The key to check.</param> <returns>Whether the given key was found.</returns> </member> - <member name="M:NetSharp.Utils.BiDictionary`2.ContainsValue(`1@)"> + <member name="M:NetSharp.Deprecated.BiDictionary`2.ContainsValue(`1@)"> <summary> Whether this instance contains the given value. </summary> <param name="value">The value to check.</param> <returns>Whether the given value was found.</returns> </member> - <member name="M:NetSharp.Utils.BiDictionary`2.SetOrUpdateKey(`1,`0)"> + <member name="M:NetSharp.Deprecated.BiDictionary`2.SetOrUpdateKey(`1,`0)"> <summary> Attempts to set the key associated with the given value. </summary> @@ -2179,7 +2200,7 @@ <param name="key">The new value for the value's associated key.</param> <returns>Whether the new key was correctly set.</returns> </member> - <member name="M:NetSharp.Utils.BiDictionary`2.SetOrUpdateValue(`0,`1)"> + <member name="M:NetSharp.Deprecated.BiDictionary`2.SetOrUpdateValue(`0,`1)"> <summary> Attempts to set the value associated with the given key. </summary> @@ -2187,7 +2208,7 @@ <param name="value">The new value for the key's associated value.</param> <returns>Whether the new value was correctly set.</returns> </member> - <member name="M:NetSharp.Utils.BiDictionary`2.TryClearKey(`1@,`0@)"> + <member name="M:NetSharp.Deprecated.BiDictionary`2.TryClearKey(`1@,`0@)"> <summary> Attempts to remove the key associated with the given value. </summary> @@ -2195,7 +2216,7 @@ <param name="key">The old key value.</param> <returns>Whether the given value had a valid key associated with it.</returns> </member> - <member name="M:NetSharp.Utils.BiDictionary`2.TryClearValue(`0@,`1@)"> + <member name="M:NetSharp.Deprecated.BiDictionary`2.TryClearValue(`0@,`1@)"> <summary> Attempts to remove the value associated with the given key. </summary> @@ -2203,7 +2224,7 @@ <param name="value">The old value.</param> <returns>Whether the given key had a valid valid associated with it.</returns> </member> - <member name="M:NetSharp.Utils.BiDictionary`2.TryGetKey(`1@,`0@)"> + <member name="M:NetSharp.Deprecated.BiDictionary`2.TryGetKey(`1@,`0@)"> <summary> Attempts to get the key associated with the given value. </summary> @@ -2211,7 +2232,7 @@ <param name="key">The returned key.</param> <returns>Whether the given value has a valid key associated with it.</returns> </member> - <member name="M:NetSharp.Utils.BiDictionary`2.TryGetValue(`0@,`1@)"> + <member name="M:NetSharp.Deprecated.BiDictionary`2.TryGetValue(`0@,`1@)"> <summary> Attempts to get the value associated with the given key. </summary> @@ -2219,7 +2240,7 @@ <param name="value">The returned value.</param> <returns>Whether the given key as a valid value associated with it.</returns> </member> - <member name="M:NetSharp.Utils.BiDictionary`2.TrySetKey(`1@,`0@)"> + <member name="M:NetSharp.Deprecated.BiDictionary`2.TrySetKey(`1@,`0@)"> <summary> Attempts to set the key associated with the given value. </summary> @@ -2227,7 +2248,7 @@ <param name="key">The key which should be set for the given value.</param> <returns>Whether the given value was successfully set.</returns> </member> - <member name="M:NetSharp.Utils.BiDictionary`2.TrySetValue(`0@,`1@)"> + <member name="M:NetSharp.Deprecated.BiDictionary`2.TrySetValue(`0@,`1@)"> <summary> Attempts to set the value associated with the given key. </summary> @@ -2235,86 +2256,94 @@ <param name="value">The value which should be set for the given key.</param> <returns>Whether the given key was successfully set.</returns> </member> - <member name="T:NetSharp.Utils.Constants"> - <summary> - Holds internal default configurations and constants. - </summary> - </member> - <member name="F:NetSharp.Utils.Constants.DefaultPort"> - <summary> - The default port over which a connection is made. - </summary> - </member> - <member name="T:NetSharp.Utils.Conversion.EndianAwareBitConverter"> + <member name="T:NetSharp.Deprecated.Conversion.EndianAwareBitConverter"> <summary> Wraps the <see cref="T:System.BitConverter"/> class to provide conversion that is endian-aware. </summary> </member> - <member name="M:NetSharp.Utils.Conversion.EndianAwareBitConverter.ReverseAsNeeded(System.Span{System.Byte},System.Boolean)"> + <member name="M:NetSharp.Deprecated.Conversion.EndianAwareBitConverter.ReverseAsNeeded(System.Span{System.Byte},System.Boolean)"> <summary> Reverses the given bytes if the endian-nes doesn't match. </summary> </member> - <member name="M:NetSharp.Utils.Conversion.EndianAwareBitConverter.GetBytes(System.Boolean,System.Boolean)"> + <member name="M:NetSharp.Deprecated.Conversion.EndianAwareBitConverter.GetBytes(System.Boolean,System.Boolean)"> <inheritdoc cref="M:System.BitConverter.GetBytes(System.Boolean)"/> </member> - <member name="M:NetSharp.Utils.Conversion.EndianAwareBitConverter.GetBytes(System.Char,System.Boolean)"> + <member name="M:NetSharp.Deprecated.Conversion.EndianAwareBitConverter.GetBytes(System.Char,System.Boolean)"> <inheritdoc cref="M:System.BitConverter.GetBytes(System.Char)"/> </member> - <member name="M:NetSharp.Utils.Conversion.EndianAwareBitConverter.GetBytes(System.Double,System.Boolean)"> + <member name="M:NetSharp.Deprecated.Conversion.EndianAwareBitConverter.GetBytes(System.Double,System.Boolean)"> <inheritdoc cref="M:System.BitConverter.GetBytes(System.Double)"/> </member> - <member name="M:NetSharp.Utils.Conversion.EndianAwareBitConverter.GetBytes(System.Single,System.Boolean)"> + <member name="M:NetSharp.Deprecated.Conversion.EndianAwareBitConverter.GetBytes(System.Single,System.Boolean)"> <inheritdoc cref="M:System.BitConverter.GetBytes(System.Single)"/> </member> - <member name="M:NetSharp.Utils.Conversion.EndianAwareBitConverter.GetBytes(System.Int32,System.Boolean)"> + <member name="M:NetSharp.Deprecated.Conversion.EndianAwareBitConverter.GetBytes(System.Int32,System.Boolean)"> <inheritdoc cref="M:System.BitConverter.GetBytes(System.Int32)"/> </member> - <member name="M:NetSharp.Utils.Conversion.EndianAwareBitConverter.GetBytes(System.Int64,System.Boolean)"> + <member name="M:NetSharp.Deprecated.Conversion.EndianAwareBitConverter.GetBytes(System.Int64,System.Boolean)"> <inheritdoc cref="M:System.BitConverter.GetBytes(System.Int64)"/> </member> - <member name="M:NetSharp.Utils.Conversion.EndianAwareBitConverter.GetBytes(System.Int16,System.Boolean)"> + <member name="M:NetSharp.Deprecated.Conversion.EndianAwareBitConverter.GetBytes(System.Int16,System.Boolean)"> <inheritdoc cref="M:System.BitConverter.GetBytes(System.Int16)"/> </member> - <member name="M:NetSharp.Utils.Conversion.EndianAwareBitConverter.GetBytes(System.UInt32,System.Boolean)"> + <member name="M:NetSharp.Deprecated.Conversion.EndianAwareBitConverter.GetBytes(System.UInt32,System.Boolean)"> <inheritdoc cref="M:System.BitConverter.GetBytes(System.UInt32)"/> </member> - <member name="M:NetSharp.Utils.Conversion.EndianAwareBitConverter.GetBytes(System.UInt64,System.Boolean)"> + <member name="M:NetSharp.Deprecated.Conversion.EndianAwareBitConverter.GetBytes(System.UInt64,System.Boolean)"> <inheritdoc cref="M:System.BitConverter.GetBytes(System.UInt64)"/> </member> - <member name="M:NetSharp.Utils.Conversion.EndianAwareBitConverter.GetBytes(System.UInt16,System.Boolean)"> + <member name="M:NetSharp.Deprecated.Conversion.EndianAwareBitConverter.GetBytes(System.UInt16,System.Boolean)"> <inheritdoc cref="M:System.BitConverter.GetBytes(System.UInt16)"/> </member> - <member name="M:NetSharp.Utils.Conversion.EndianAwareBitConverter.ToBoolean(System.Span{System.Byte},System.Boolean)"> + <member name="M:NetSharp.Deprecated.Conversion.EndianAwareBitConverter.ToBoolean(System.Span{System.Byte},System.Boolean)"> <inheritdoc cref="M:System.BitConverter.ToBoolean(System.ReadOnlySpan{System.Byte})"/> </member> - <member name="M:NetSharp.Utils.Conversion.EndianAwareBitConverter.ToChar(System.Span{System.Byte},System.Boolean)"> + <member name="M:NetSharp.Deprecated.Conversion.EndianAwareBitConverter.ToChar(System.Span{System.Byte},System.Boolean)"> <inheritdoc cref="M:System.BitConverter.ToChar(System.ReadOnlySpan{System.Byte})"/> </member> - <member name="M:NetSharp.Utils.Conversion.EndianAwareBitConverter.ToDouble(System.Span{System.Byte},System.Boolean)"> + <member name="M:NetSharp.Deprecated.Conversion.EndianAwareBitConverter.ToDouble(System.Span{System.Byte},System.Boolean)"> <inheritdoc cref="M:System.BitConverter.ToDouble(System.ReadOnlySpan{System.Byte})"/> </member> - <member name="M:NetSharp.Utils.Conversion.EndianAwareBitConverter.ToInt16(System.Span{System.Byte},System.Boolean)"> + <member name="M:NetSharp.Deprecated.Conversion.EndianAwareBitConverter.ToInt16(System.Span{System.Byte},System.Boolean)"> <inheritdoc cref="M:System.BitConverter.ToInt16(System.ReadOnlySpan{System.Byte})"/> </member> - <member name="M:NetSharp.Utils.Conversion.EndianAwareBitConverter.ToInt32(System.Span{System.Byte},System.Boolean)"> + <member name="M:NetSharp.Deprecated.Conversion.EndianAwareBitConverter.ToInt32(System.Span{System.Byte},System.Boolean)"> <inheritdoc cref="M:System.BitConverter.ToInt32(System.ReadOnlySpan{System.Byte})"/> </member> - <member name="M:NetSharp.Utils.Conversion.EndianAwareBitConverter.ToInt64(System.Span{System.Byte},System.Boolean)"> + <member name="M:NetSharp.Deprecated.Conversion.EndianAwareBitConverter.ToInt64(System.Span{System.Byte},System.Boolean)"> <inheritdoc cref="M:System.BitConverter.ToInt64(System.ReadOnlySpan{System.Byte})"/> </member> - <member name="M:NetSharp.Utils.Conversion.EndianAwareBitConverter.ToSingle(System.Span{System.Byte},System.Boolean)"> + <member name="M:NetSharp.Deprecated.Conversion.EndianAwareBitConverter.ToSingle(System.Span{System.Byte},System.Boolean)"> <inheritdoc cref="M:System.BitConverter.ToSingle(System.ReadOnlySpan{System.Byte})"/> </member> - <member name="M:NetSharp.Utils.Conversion.EndianAwareBitConverter.ToUInt16(System.Byte[],System.Boolean)"> + <member name="M:NetSharp.Deprecated.Conversion.EndianAwareBitConverter.ToUInt16(System.Byte[],System.Boolean)"> <inheritdoc cref="M:System.BitConverter.ToUInt16(System.ReadOnlySpan{System.Byte})"/> </member> - <member name="M:NetSharp.Utils.Conversion.EndianAwareBitConverter.ToUInt32(System.Span{System.Byte},System.Boolean)"> + <member name="M:NetSharp.Deprecated.Conversion.EndianAwareBitConverter.ToUInt32(System.Span{System.Byte},System.Boolean)"> <inheritdoc cref="M:System.BitConverter.ToUInt32(System.ReadOnlySpan{System.Byte})"/> </member> - <member name="M:NetSharp.Utils.Conversion.EndianAwareBitConverter.ToUInt64(System.Span{System.Byte},System.Boolean)"> + <member name="M:NetSharp.Deprecated.Conversion.EndianAwareBitConverter.ToUInt64(System.Span{System.Byte},System.Boolean)"> <inheritdoc cref="M:System.BitConverter.ToUInt64(System.ReadOnlySpan{System.Byte})"/> </member> + <member name="M:NetSharp.Packets.NetworkPacket.#ctor(NetSharp.Packets.NetworkPacket.NetworkPacketHeader,System.ReadOnlyMemory{System.Byte},NetSharp.Packets.NetworkPacket.NetworkPacketFooter)"> + <summary> + Constructs a new instance of the <see cref="T:NetSharp.Packets.NetworkPacket"/> struct. + </summary> + <param name="packetHeader">The header for this packet.</param> + <param name="packetDataBuffer">The data that should be stored in the packet.</param> + <param name="packetFooter">The footer for this packet.</param> + <exception cref="T:System.ArgumentException"> + Thrown when the given <paramref name="packetDataBuffer"/> exceeds <see cref="F:NetSharp.Packets.NetworkPacket.TotalSize"/> bytes in size. + </exception> + </member> + <member name="M:NetSharp.Sockets.SocketAsyncOperations.HandleIoCompleted(System.Object,System.Net.Sockets.SocketAsyncEventArgs)"> + <summary> + Event handler for the <see cref="E:System.Net.Sockets.SocketAsyncEventArgs.Completed"/> event. + </summary> + <param name="sender">The object on which the event is raised.</param> + <param name="args">The event arguments.</param> + </member> <member name="T:NetSharp.Utils.TransmissionResult"> <summary> Represents the result of a socket transmission. diff --git a/NetSharp/NetSharp/Packets/Builtin/ConnectPacket.cs b/NetSharp/NetSharp/Packets/Builtin/ConnectPacket.cs @@ -1,33 +0,0 @@ -using System; -using NetSharp.Deprecated; - -namespace NetSharp.Packets.Builtin -{ - /// <summary> - /// A simple connection request packet for the UDP protocol. - /// </summary> - [PacketTypeId(1)] - internal class ConnectPacket : IRequestPacket - { - /// <inheritdoc /> - public void AfterDeserialisation() - { - } - - /// <inheritdoc /> - public void BeforeSerialisation() - { - } - - /// <inheritdoc /> - public void Deserialise(ReadOnlyMemory<byte> serialisedObject) - { - } - - /// <inheritdoc /> - public Memory<byte> Serialise() - { - return Memory<byte>.Empty; - } - } -} -\ No newline at end of file diff --git a/NetSharp/NetSharp/Packets/Builtin/ConnectResponsePacket.cs b/NetSharp/NetSharp/Packets/Builtin/ConnectResponsePacket.cs @@ -1,36 +0,0 @@ -using System; -using NetSharp.Deprecated; - -namespace NetSharp.Packets.Builtin -{ - /// <summary> - /// A response packet for the <see cref="ConnectPacket"/>. - /// </summary> - [PacketTypeId(2)] - internal class ConnectResponsePacket : IResponsePacket<ConnectPacket> - { - /// <inheritdoc /> - public ConnectPacket RequestPacket { get; set; } = new ConnectPacket(); - - /// <inheritdoc /> - public void AfterDeserialisation() - { - } - - /// <inheritdoc /> - public void BeforeSerialisation() - { - } - - /// <inheritdoc /> - public void Deserialise(ReadOnlyMemory<byte> serialisedObject) - { - } - - /// <inheritdoc /> - public Memory<byte> Serialise() - { - return Memory<byte>.Empty; - } - } -} -\ No newline at end of file diff --git a/NetSharp/NetSharp/Packets/Builtin/DataPacket.cs b/NetSharp/NetSharp/Packets/Builtin/DataPacket.cs @@ -1,56 +0,0 @@ -using System; -using NetSharp.Deprecated; - -namespace NetSharp.Packets.Builtin -{ - /// <summary> - /// A simple data transfer packet, that allows for the transmission of an arbitrary number of frames. - /// </summary> - [PacketTypeId(5)] - public class DataPacket : IRequestPacket - { - /// <summary> - /// The data that should be transferred across the network. - /// </summary> - public Memory<byte> RequestBuffer; - - /// <summary> - /// Initialises a new instance of the <see cref="DataPacket"/> class. - /// </summary> - public DataPacket() - { - RequestBuffer = new byte[0]; - } - - /// <summary> - /// Initialises a new instance of the <see cref="DataPacket"/> class. - /// </summary> - /// <param name="buffer">The data that this request packet should contain.</param> - public DataPacket(Memory<byte> buffer) - { - RequestBuffer = buffer; - } - - /// <inheritdoc /> - public void AfterDeserialisation() - { - } - - /// <inheritdoc /> - public void BeforeSerialisation() - { - } - - /// <inheritdoc /> - public void Deserialise(ReadOnlyMemory<byte> serialisedObject) - { - RequestBuffer = serialisedObject.ToArray(); - } - - /// <inheritdoc /> - public Memory<byte> Serialise() - { - return RequestBuffer; - } - } -} -\ No newline at end of file diff --git a/NetSharp/NetSharp/Packets/Builtin/DataResponsePacket.cs b/NetSharp/NetSharp/Packets/Builtin/DataResponsePacket.cs @@ -1,59 +0,0 @@ -using System; -using NetSharp.Deprecated; - -namespace NetSharp.Packets.Builtin -{ - /// <summary> - /// A response packet for the <see cref="DataPacket"/>. - /// </summary> - [PacketTypeId(6)] - public class DataResponsePacket : IResponsePacket<DataPacket> - { - /// <summary> - /// The data that should be transferred across the network. - /// </summary> - public Memory<byte> ResponseBuffer; - - /// <summary> - /// Initialises a new instance of the <see cref="DataResponsePacket"/> class. - /// </summary> - public DataResponsePacket() - { - ResponseBuffer = new byte[0]; - } - - /// <summary> - /// Initialises a new instance of the <see cref="DataResponsePacket"/> class. - /// </summary> - /// <param name="buffer">The data that this response packet should contain.</param> - public DataResponsePacket(Memory<byte> buffer) - { - ResponseBuffer = buffer; - } - - /// <inheritdoc /> - public DataPacket RequestPacket { get; internal set; } = new DataPacket(); - - /// <inheritdoc /> - public void AfterDeserialisation() - { - } - - /// <inheritdoc /> - public void BeforeSerialisation() - { - } - - /// <inheritdoc /> - public void Deserialise(ReadOnlyMemory<byte> serialisedObject) - { - ResponseBuffer = serialisedObject.ToArray(); - } - - /// <inheritdoc /> - public Memory<byte> Serialise() - { - return ResponseBuffer; - } - } -} -\ No newline at end of file diff --git a/NetSharp/NetSharp/Packets/Builtin/DisconnectPacket.cs b/NetSharp/NetSharp/Packets/Builtin/DisconnectPacket.cs @@ -1,33 +0,0 @@ -using System; -using NetSharp.Deprecated; - -namespace NetSharp.Packets.Builtin -{ - /// <summary> - /// A simple disconnect packet for the UDP protocol. - /// </summary> - [PacketTypeId(0)] - internal class DisconnectPacket : IRequestPacket - { - /// <inheritdoc /> - public void AfterDeserialisation() - { - } - - /// <inheritdoc /> - public void BeforeSerialisation() - { - } - - /// <inheritdoc /> - public void Deserialise(ReadOnlyMemory<byte> serialisedObject) - { - } - - /// <inheritdoc /> - public Memory<byte> Serialise() - { - return Memory<byte>.Empty; - } - } -} -\ No newline at end of file diff --git a/NetSharp/NetSharp/Packets/Builtin/PingPacket.cs b/NetSharp/NetSharp/Packets/Builtin/PingPacket.cs @@ -1,33 +0,0 @@ -using System; -using NetSharp.Deprecated; - -namespace NetSharp.Packets.Builtin -{ - /// <summary> - /// A simple ping request packet for heartbeat monitoring and RTT measurement. - /// </summary> - [PacketTypeId(3)] - public class PingPacket : IRequestPacket - { - /// <inheritdoc /> - public void AfterDeserialisation() - { - } - - /// <inheritdoc /> - public void BeforeSerialisation() - { - } - - /// <inheritdoc /> - public void Deserialise(ReadOnlyMemory<byte> serialisedObject) - { - } - - /// <inheritdoc /> - public Memory<byte> Serialise() - { - return Memory<byte>.Empty; - } - } -} -\ No newline at end of file diff --git a/NetSharp/NetSharp/Packets/Builtin/PingResponsePacket.cs b/NetSharp/NetSharp/Packets/Builtin/PingResponsePacket.cs @@ -1,36 +0,0 @@ -using System; -using NetSharp.Deprecated; - -namespace NetSharp.Packets.Builtin -{ - /// <summary> - /// A response packet for the <see cref="PingPacket"/>. - /// </summary> - [PacketTypeId(4)] - public class PingResponsePacket : IResponsePacket<PingPacket> - { - /// <inheritdoc /> - public PingPacket RequestPacket { get; internal set; } = new PingPacket(); - - /// <inheritdoc /> - public void AfterDeserialisation() - { - } - - /// <inheritdoc /> - public void BeforeSerialisation() - { - } - - /// <inheritdoc /> - public void Deserialise(ReadOnlyMemory<byte> serialisedObject) - { - } - - /// <inheritdoc /> - public Memory<byte> Serialise() - { - return Memory<byte>.Empty; - } - } -} -\ No newline at end of file diff --git a/NetSharp/NetSharp/Packets/Builtin/SimpleDataPacket.cs b/NetSharp/NetSharp/Packets/Builtin/SimpleDataPacket.cs @@ -1,56 +0,0 @@ -using System; -using NetSharp.Deprecated; - -namespace NetSharp.Packets.Builtin -{ - /// <summary> - /// A simple one-time-use data transfer packet, that allows for the transmission of an arbitrary number of frames. - /// </summary> - [PacketTypeId(7)] - public class SimpleDataPacket : IRequestPacket - { - /// <summary> - /// The data that should be transferred across the network. - /// </summary> - public Memory<byte> RequestBuffer; - - /// <summary> - /// Initialises a new instance of the <see cref="SimpleDataPacket"/> class. - /// </summary> - public SimpleDataPacket() - { - RequestBuffer = new byte[0]; - } - - /// <summary> - /// Initialises a new instance of the <see cref="SimpleDataPacket"/> class. - /// </summary> - /// <param name="buffer">The data that this request packet should contain.</param> - public SimpleDataPacket(Memory<byte> buffer) - { - RequestBuffer = buffer; - } - - /// <inheritdoc /> - public void AfterDeserialisation() - { - } - - /// <inheritdoc /> - public void BeforeSerialisation() - { - } - - /// <inheritdoc /> - public void Deserialise(ReadOnlyMemory<byte> serialisedObject) - { - RequestBuffer = serialisedObject.ToArray(); - } - - /// <inheritdoc /> - public Memory<byte> Serialise() - { - return RequestBuffer; - } - } -} -\ No newline at end of file diff --git a/NetSharp/NetSharp/Packets/NetworkErrorCode.cs b/NetSharp/NetSharp/Packets/NetworkErrorCode.cs @@ -1,18 +0,0 @@ -namespace NetSharp.Packets -{ - /// <summary> - /// Enumerates the possible error codes for network operations, being held in the packet. - /// </summary> - public enum NetworkErrorCode : uint - { - /// <summary> - /// Signifies that there was no error during transmission. - /// </summary> - Ok = 0, - - /// <summary> - /// A generic error occurred during packet transmission. - /// </summary> - Error = 1 << 1, - } -} -\ No newline at end of file diff --git a/NetSharp/NetSharp/Packets/NetworkPacket.cs b/NetSharp/NetSharp/Packets/NetworkPacket.cs @@ -1,217 +1,95 @@ using System; -using NetSharp.Utils.Conversion; namespace NetSharp.Packets { - /// <summary> - /// Represents a low-level packet that is transmitted over the network. - /// </summary> public readonly struct NetworkPacket { - /// <summary> - /// Initialises a new instance of the <see cref="NetworkPacket"/> struct. - /// </summary> - /// <param name="data">The data that should be transmitted in the packet.</param> - /// <param name="header">The header for the packet.</param> - /// <param name="footer">The footer for the packet.</param> - private NetworkPacket(ReadOnlyMemory<byte> data, NetworkPacketHeader header, NetworkPacketFooter footer) - { - Header = header; + public const int TotalSize = 4096; - DataBuffer = data; + public const int HeaderSize = NetworkPacketHeader.TotalSize; - Footer = footer; - } + public const int FooterSize = NetworkPacketFooter.TotalSize; - /// <summary> - /// The number of bytes allocated in each packet for user data. - /// </summary> - public const int DataSegmentSize = PacketSize - HeaderSize - FooterSize; + public const int DataSize = TotalSize - HeaderSize - FooterSize; - /// <summary> - /// The number of bytes taken up in each packet by its footer. - /// </summary> - public const int FooterSize = NetworkPacketFooter.Size; + public readonly ReadOnlyMemory<byte> Data; - /// <summary> - /// The number of bytes taken up in each packet by its header. - /// </summary> - public const int HeaderSize = NetworkPacketHeader.Size; - - /// <summary> - /// The size of each packet, including its header, footer, and data segment. - /// </summary> - public const int PacketSize = 4096; - - /// <summary> - /// The data held in this packet. - /// </summary> - public readonly ReadOnlyMemory<byte> DataBuffer; - - public readonly NetworkPacketFooter Footer; public readonly NetworkPacketHeader Header; - /// <summary> - /// Initialises a new instance of the <see cref="NetworkPacket"/> struct. - /// </summary> - /// <param name="data">The data that should be transmitted in the packet.</param> - /// <param name="dataLength">The number of bytes that are held in the given data buffer.</param> - /// <param name="type">The packet type.</param> - /// <param name="errorCode">The error code associated with this transmission.</param> - /// <param name="hasSucceedingPacket">Whether this packet has a succeeding packet in the packet chain.</param> - public NetworkPacket(ReadOnlyMemory<byte> data, int dataLength, uint type, NetworkErrorCode errorCode, bool hasSucceedingPacket) - { - Header = new NetworkPacketHeader(type, errorCode, dataLength); - - DataBuffer = data; - - Footer = new NetworkPacketFooter(hasSucceedingPacket); - } - - /// <summary> - /// Deserialises the given buffer into a packet instance. - /// </summary> - /// <param name="buffer">The byte buffer to serialise.</param> - /// <returns>The deserialised packet instance.</returns> - public static NetworkPacket Deserialise(Memory<byte> buffer) - { - Span<byte> serialisedPacketHeader = buffer.Slice(0, HeaderSize).Span; - NetworkPacketHeader header = NetworkPacketHeader.Deserialise(serialisedPacketHeader); - - Span<byte> serialisedPacketFooter = buffer.Slice(HeaderSize + DataSegmentSize, FooterSize).Span; - NetworkPacketFooter footer = NetworkPacketFooter.Deserialise(serialisedPacketFooter); - - Memory<byte> serialisedInstanceData = buffer.Slice(HeaderSize, DataSegmentSize); - - return new NetworkPacket(serialisedInstanceData, header, footer); - } - - /// <summary> - /// Serialises the given packet instance to a new byte buffer. - /// </summary> - /// <param name="instance">The packet instance to serialise.</param> - /// <returns>The byte buffer that represents the packet instance.</returns> - public static Memory<byte> Serialise(NetworkPacket instance) - { - byte[] buffer = new byte[PacketSize]; - SerialiseToBuffer(buffer, instance); - return buffer; - } + public readonly NetworkPacketFooter Footer; /// <summary> - /// Serialises the given packet instance into the given byte buffer. + /// Constructs a new instance of the <see cref="NetworkPacket"/> struct. /// </summary> - /// <param name="buffer"> - /// The buffer to which the instance should be serialised. Must be at least of size <see cref="PacketSize"/>. - /// </param> - /// <param name="instance">The packet instance to serialise.</param> - /// <exception cref="ArgumentException">Thrown if the given buffer is too small.</exception> - public static void SerialiseToBuffer(Memory<byte> buffer, NetworkPacket instance) + /// <param name="packetHeader">The header for this packet.</param> + /// <param name="packetDataBuffer">The data that should be stored in the packet.</param> + /// <param name="packetFooter">The footer for this packet.</param> + /// <exception cref="ArgumentException"> + /// Thrown when the given <paramref name="packetDataBuffer"/> exceeds <see cref="TotalSize"/> bytes in size. + /// </exception> + private NetworkPacket(NetworkPacketHeader packetHeader, ReadOnlyMemory<byte> packetDataBuffer, NetworkPacketFooter packetFooter) { - if (buffer.Length < PacketSize) + if (packetDataBuffer.Length > DataSize) { - throw new ArgumentException("Given buffer is too small to serialise the packet instance into.", nameof(buffer)); + throw new ArgumentException( + $"Given buffer exceeds {TotalSize} bytes, and cannot fit into a network packet", + nameof(packetDataBuffer)); } - Span<byte> serialisedPacketHeader = buffer.Slice(0, HeaderSize).Span; - NetworkPacketHeader.Serialise(serialisedPacketHeader, instance.Header); - - Span<byte> serialisedPacketFooter = buffer.Slice(HeaderSize + DataSegmentSize, FooterSize).Span; - NetworkPacketFooter.Serialise(serialisedPacketFooter, instance.Footer); - - Memory<byte> serialisedInstanceData = buffer.Slice(HeaderSize, DataSegmentSize); - instance.DataBuffer.CopyTo(serialisedInstanceData); + Data = packetDataBuffer; } - } - // TODO: Document - public readonly struct NetworkPacketFooter - { - private const int PacketHasNextStart = 0; + public static NetworkPacket Deserialise(ReadOnlyMemory<byte> buffer) + { + ReadOnlyMemory<byte> serialisedPacketHeader = buffer.Slice(0, HeaderSize); + NetworkPacketHeader packetHeader = NetworkPacketHeader.Deserialise(serialisedPacketHeader); - /// <summary> - /// The number of bytes taken up by a packet footer. - /// </summary> - public const int Size = sizeof(bool); + ReadOnlyMemory<byte> packetDataBuffer = buffer.Slice(HeaderSize, DataSize); - public readonly bool HasSucceedingPacket; + ReadOnlyMemory<byte> serialisedPacketFooter = buffer.Slice(HeaderSize + DataSize, FooterSize); + NetworkPacketFooter packetFooter = NetworkPacketFooter.Deserialise(serialisedPacketFooter); - public NetworkPacketFooter(bool hasSucceedingPacket) - { - HasSucceedingPacket = hasSucceedingPacket; + return new NetworkPacket(packetHeader, packetDataBuffer, packetFooter); } - public static NetworkPacketFooter Deserialise(Span<byte> buffer) + public static void Serialise(NetworkPacket instance, Memory<byte> buffer) { - Span<byte> serialisedHasNextFlag = buffer.Slice(PacketHasNextStart, sizeof(bool)); + Memory<byte> packetHeader = buffer.Slice(0, HeaderSize); + NetworkPacketHeader.Serialise(instance.Header, packetHeader); - return new NetworkPacketFooter( - EndianAwareBitConverter.ToBoolean(serialisedHasNextFlag)); - } + Memory<byte> packetDataBuffer = buffer.Slice(HeaderSize, DataSize); + instance.Data.CopyTo(packetDataBuffer); - public static void Serialise(Span<byte> buffer, NetworkPacketFooter instance) - { - Span<byte> serialisedHasNextFlag = buffer.Slice(PacketHasNextStart, sizeof(bool)); - - EndianAwareBitConverter.GetBytes(instance.HasSucceedingPacket).CopyTo(serialisedHasNextFlag); + Memory<byte> packetFooter = buffer.Slice(HeaderSize + DataSize, FooterSize); + NetworkPacketFooter.Serialise(instance.Footer, packetFooter); } - } - - // TODO: Document - public readonly struct NetworkPacketHeader - { - private const int PacketDataLengthStart = 2 * sizeof(uint); - private const int PacketErrorCodeStart = sizeof(uint); - private const int PacketTypeStart = 0; - /// <summary> - /// The number of bytes taken up by a packet header. - /// </summary> - public const int Size = sizeof(uint) + sizeof(uint) + sizeof(int); - - /// <summary> - /// The number of bytes of data held in the packet. - /// </summary> - public readonly int DataLength; - - /// <summary> - /// The error code for this packet. - /// </summary> - public readonly NetworkErrorCode ErrorCode; + public readonly struct NetworkPacketHeader + { + public const int TotalSize = 0; - /// <summary> - /// The packet type. - /// </summary> - public readonly uint Type; + public static NetworkPacketHeader Deserialise(ReadOnlyMemory<byte> buffer) + { + return new NetworkPacketHeader(); + } - public NetworkPacketHeader(uint packetType, NetworkErrorCode packetErrorCode, int packetDataLength) - { - Type = packetType; - ErrorCode = packetErrorCode; - DataLength = packetDataLength; + public static void Serialise(NetworkPacketHeader instance, Memory<byte> buffer) + { + } } - public static NetworkPacketHeader Deserialise(Span<byte> buffer) + public readonly struct NetworkPacketFooter { - Span<byte> serialisedType = buffer.Slice(PacketTypeStart, sizeof(uint)); - Span<byte> serialisedErrorCode = buffer.Slice(PacketErrorCodeStart, sizeof(uint)); - Span<byte> serialisedDataLength = buffer.Slice(PacketDataLengthStart, sizeof(int)); - - return new NetworkPacketHeader( - EndianAwareBitConverter.ToUInt32(serialisedType), - (NetworkErrorCode)EndianAwareBitConverter.ToUInt32(serialisedErrorCode), - EndianAwareBitConverter.ToInt32(serialisedDataLength)); - } + public const int TotalSize = 0; - public static void Serialise(Span<byte> buffer, NetworkPacketHeader instance) - { - Span<byte> serialisedType = buffer.Slice(PacketTypeStart, sizeof(uint)); - Span<byte> serialisedErrorCode = buffer.Slice(PacketErrorCodeStart, sizeof(uint)); - Span<byte> serialisedDataLength = buffer.Slice(PacketDataLengthStart, sizeof(int)); + public static NetworkPacketFooter Deserialise(ReadOnlyMemory<byte> buffer) + { + return new NetworkPacketFooter(); + } - EndianAwareBitConverter.GetBytes(instance.Type).CopyTo(serialisedType); - EndianAwareBitConverter.GetBytes((uint)instance.ErrorCode).CopyTo(serialisedErrorCode); - EndianAwareBitConverter.GetBytes(instance.DataLength).CopyTo(serialisedDataLength); + public static void Serialise(NetworkPacketFooter instance, Memory<byte> buffer) + { + } } } } \ No newline at end of file diff --git a/NetSharp/NetSharp/Packets/PacketRegistry.cs b/NetSharp/NetSharp/Packets/PacketRegistry.cs @@ -1,287 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Runtime.CompilerServices; -using NetSharp.Deprecated; -using NetSharp.Utils; - -namespace NetSharp.Packets -{ - /// <summary> - /// Provides method of registering request packets and their relevant response packets, as well as mapping their ids. - /// </summary> - internal static class PacketRegistry - { - /// <summary> - /// The start id for automatically generated packet type ids. Any custom packet type ids lower than this value - /// that come from external assemblies will be incremented by this value, to ensure that there are no clashes. - /// </summary> - private const uint AutomaticPacketTypeIdStartPoint = 100; - - /// <summary> - /// The lock object for synchronising access to the <see cref="currentAutomaticPacketTypeIdCounter"/> field. - /// </summary> - private static readonly object currentAutomaticPacketTypeIdCounterLockObject = new object(); - - /// <summary> - /// Maps a packet type id to its relevant packet type, and vice-versa. - /// </summary> - private static readonly BiDictionary<uint, Type> idToPacketTypeMap; - - /// <summary> - /// The assembly that represents the library, where all of the builtin packets are defined. - /// </summary> - private static readonly Assembly LibraryAssembly = Assembly.GetAssembly(typeof(PacketRegistry)); - - /// <summary> - /// Maps a request packet to its relevant response packet, and vice-versa. - /// </summary> - private static readonly BiDictionary<Type, Type> requestToResponseMap; - - /// <summary> - /// The current id for registered packets. - /// </summary> - private static uint currentAutomaticPacketTypeIdCounter = AutomaticPacketTypeIdStartPoint; - - /// <summary> - /// Fetches the packet type id of the given packet type. If the packet type is declared outside of the library - /// assembly, then its value is incremented by the <see cref="AutomaticPacketTypeIdStartPoint"/> value. This ensure that - /// there are no clashes between the packet type ids of packets declared in the library and external packets. - /// </summary> - /// <param name="packetType">The packet type whose id should be fetched.</param> - /// <returns>The id of the given packet type.</returns> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static uint GetNewPacketTypeId(Type packetType) - { - uint packetTypeId; - - if (packetType.Assembly != LibraryAssembly) - { - lock (currentAutomaticPacketTypeIdCounterLockObject) - { - packetTypeId = currentAutomaticPacketTypeIdCounter++; - } - } - else - { - PacketTypeIdAttribute customPacketTypeIdAttribute = - (PacketTypeIdAttribute)packetType.GetCustomAttributes(typeof(PacketTypeIdAttribute)).First(); - - packetTypeId = customPacketTypeIdAttribute.Id; - } - - return packetTypeId; - } - - /// <summary> - /// Deregisters the given packet type from the registry. - /// </summary> - /// <param name="requestPacketType">The request packet type to deregister, if it is registered.</param> - /// <param name="responsePacketType">The response packet associated with the request packet.</param> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static void DeregisterPacketType(Type requestPacketType, Type? responsePacketType) - { - if (idToPacketTypeMap.ContainsValue(requestPacketType)) - { - idToPacketTypeMap.TryClearKey(requestPacketType, out _); - } - - // if the given response packet is null, then skip deregistering a response packet type - if (responsePacketType == default) return; - - if (!idToPacketTypeMap.ContainsValue(responsePacketType)) - { - idToPacketTypeMap.TryClearKey(responsePacketType, out _); - } - - if (!requestToResponseMap.ContainsValue(requestPacketType)) - { - requestToResponseMap.TryClearKey(requestPacketType, out _); - } - } - - /// <summary> - /// Deregisters the given packet types from the registry. - /// </summary> - /// <param name="requestToResponsePacketTypeMap">The list of packet types to deregister, if they are registered.</param> - internal static void DeregisterPacketTypes(Dictionary<Type, Type?> requestToResponsePacketTypeMap) - { - foreach ((Type requestPacketType, Type? responsePacketType) in requestToResponsePacketTypeMap) - { - DeregisterPacketType(requestPacketType, responsePacketType); - } - } - - /// <summary> - /// Returns the packet type id associated with the given packet type. - /// </summary> - /// <param name="packetType">The packet type whose id to fetch.</param> - /// <returns>The id of the packet type given.</returns> - internal static uint GetPacketId(Type packetType) => idToPacketTypeMap[packetType]; - - /// <summary> - /// Returns the packet type id associated with the given packet type. - /// </summary> - /// <typeparam name="TPacket">The packet type whose id to fetch.</typeparam> - /// <returns>The id of the packet type given.</returns> - internal static uint GetPacketId<TPacket>() where TPacket : IPacket => idToPacketTypeMap[typeof(TPacket)]; - - /// <summary> - /// Returns the packet type associated with the given id. - /// </summary> - /// <param name="packetTypeId">The packet id whose mapped type to fetch.</param> - /// <returns>The packet type mapped by the given id.</returns> - internal static Type GetPacketType(uint packetTypeId) => idToPacketTypeMap[packetTypeId]; - - /// <summary> - /// Returns the type of request packet mapped by the given response packet type. - /// </summary> - /// <typeparam name="TResponse">The response packet type whose request packet type to fetch.</typeparam> - /// <returns>The request packet type, <c>null</c> if no type is mapped.</returns> - internal static Type GetRequestPacketType<TResponse>() where TResponse : IResponsePacket<IRequestPacket> - { - requestToResponseMap.TryGetKey(typeof(TResponse), out Type requestPacketType); - - return requestPacketType; - } - - /// <summary> - /// Returns the type of request packet mapped by the given response packet type. - /// </summary> - /// <param name="responsePacketType">The response packet type whose request packet type to fetch.</param> - /// <returns>The request packet type, <c>null</c> if no type is mapped.</returns> - internal static Type GetRequestPacketType(Type responsePacketType) - { - requestToResponseMap.TryGetKey(responsePacketType, out Type requestPacketType); - - return requestPacketType; - } - - /// <summary> - /// Returns the type of response packet mapped by the given request packet type. - /// </summary> - /// <typeparam name="TRequest">The request packet type whose response packet type to fetch.</typeparam> - /// <returns>The response packet type, <c>null</c> if no type is mapped.</returns> - internal static Type? GetResponsePacketType<TRequest>() where TRequest : IRequestPacket - { - return requestToResponseMap.TryGetValue(typeof(TRequest), out Type responsePacketType) ? responsePacketType : default; - } - - /// <summary> - /// Returns the type of response packet mapped by the given request packet type. - /// </summary> - /// <param name="requestPacketType">The request packet type whose response packet type to fetch.</param> - /// <returns>The response packet type, <c>null</c> if no type is mapped.</returns> - internal static Type? GetResponsePacketType(Type requestPacketType) - { - return requestToResponseMap.TryGetValue(requestPacketType, out Type responsePacketType) ? responsePacketType : default; - } - - /// <summary> - /// Rebuilds the packet registry, by registering every <see cref="IPacket"/> inheritor in the given assemblies. - /// </summary> - /// <param name="packetSourceAssemblies"> - /// The assemblies from which the packet types to register are sourced. - /// </param> - internal static void RegisterPacketSourceAssemblies(params Assembly[] packetSourceAssemblies) - { - foreach (Assembly assembly in packetSourceAssemblies) - { - RegisterPacketSourceAssembly(assembly); - } - } - - /// <summary> - /// Registers all the <see cref="IPacket"/> implementors in the given assembly. - /// </summary> - /// <param name="packetSourceAssembly">The assembly whose packet types to register.</param> - //[MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static void RegisterPacketSourceAssembly(Assembly packetSourceAssembly) - { - Dictionary<Type, Type?> requestToResponseTypeMap = new Dictionary<Type, Type?>(); - - foreach (Type type in packetSourceAssembly.DefinedTypes) - { - foreach (Type interfaceType in type.GetInterfaces()) - { - if (!typeof(IPacket).IsAssignableFrom(interfaceType) || interfaceType == typeof(IPacket)) - { - continue; - } - - if (interfaceType == typeof(IRequestPacket)) - { - requestToResponseTypeMap[type] = default; - } - else //if (interfaceType == typeof(IResponsePacket<>)) - { - Type handledRequestType = interfaceType.GetGenericArguments()[0]; - - requestToResponseTypeMap[handledRequestType] = type; - } - } - } - - RegisterPacketTypes(requestToResponseTypeMap); - } - - /// <summary> - /// Registers the given packet type to the registry. - /// </summary> - /// <param name="requestPacketType">The request packet type to register, if it is not registered.</param> - /// <param name="responsePacketType">The response packet associated with the request packet.</param> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static void RegisterPacketType(Type requestPacketType, Type? responsePacketType) - { - if (!idToPacketTypeMap.ContainsValue(requestPacketType)) - { - uint requestPacketTypeId = GetNewPacketTypeId(requestPacketType); - - idToPacketTypeMap.TrySetValue(requestPacketTypeId, requestPacketType); - } - - // if the given response packet is null, then skip registering a response packet type - if (responsePacketType == default) return; - - if (!idToPacketTypeMap.ContainsValue(responsePacketType)) - { - uint responsePacketTypeId = GetNewPacketTypeId(responsePacketType); - - idToPacketTypeMap.TrySetValue(responsePacketTypeId, responsePacketType); - } - - if (!requestToResponseMap.ContainsValue(requestPacketType)) - { - requestToResponseMap.TrySetValue(requestPacketType, responsePacketType); - } - } - - /// <summary> - /// Registers the given packet types to the registry. - /// </summary> - /// <param name="requestToResponsePacketTypeMap"> - /// The dictionary mapping the request packet types to register, to their relevant response packet types. - /// The response packet type can be null; then the request packet type is treated as a 'simple' packet. - /// </param> - internal static void RegisterPacketTypes(Dictionary<Type, Type?> requestToResponsePacketTypeMap) - { - foreach ((Type requestPacketType, Type? responsePacketType) in requestToResponsePacketTypeMap) - { - RegisterPacketType(requestPacketType, responsePacketType); - } - } - - /// <summary> - /// Initialises a new instance of the <see cref="PacketRegistry"/> class. - /// </summary> - static PacketRegistry() - { - idToPacketTypeMap = new BiDictionary<uint, Type>(); - - requestToResponseMap = new BiDictionary<Type, Type>(); - - RegisterPacketSourceAssembly(LibraryAssembly); - } - } -} -\ No newline at end of file diff --git a/NetSharp/NetSharp/Packets/PacketTypeIdAttribute.cs b/NetSharp/NetSharp/Packets/PacketTypeIdAttribute.cs @@ -1,27 +0,0 @@ -using System; -using NetSharp.Deprecated; - -namespace NetSharp.Packets -{ - /// <summary> - /// Allows the placing of a custom packet type on a class or struct. This is used if the class or struct - /// inherits from <see cref="IRequestPacket"/> or <see cref="IResponsePacket{TReq}"/>. - /// </summary> - [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)] - internal sealed class PacketTypeIdAttribute : Attribute - { - /// <summary> - /// Initialises a new instance of the <see cref="PacketTypeIdAttribute"/> attribute. - /// </summary> - /// <param name="type">The custom type id that the decorated packet type should have.</param> - internal PacketTypeIdAttribute(uint type) - { - Id = type; - } - - /// <summary> - /// The custom type id that the decorated packet type should have. This overrides the automatically generated id. - /// </summary> - internal uint Id { get; } - } -} -\ No newline at end of file diff --git a/NetSharp/NetSharp/Pipelines/PacketPipeline.cs b/NetSharp/NetSharp/Pipelines/PacketPipeline.cs @@ -1,65 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; - -namespace NetSharp.Pipelines -{ - /// <summary> - /// Represents a pipeline of transformations that packets must undergo. - /// </summary> - /// <typeparam name="TInput">The type of packet the pipeline receives.</typeparam> - /// <typeparam name="TIntermediate">The type of packet the pipeline internally handles.</typeparam> - /// <typeparam name="TOutput">The type of packet the pipeline outputs.</typeparam> - // TODO: Implement a packet pipeline, with multiple transform stages to allow encryption, compression, and various other bytewise manipulation stages. - internal readonly struct PacketPipeline<TInput, TIntermediate, TOutput> - { - private readonly PacketPipelineStage<TInput, TIntermediate> pipelineInputStage; - private readonly IReadOnlyCollection<PacketPipelineStage<TIntermediate, TIntermediate>> pipelineIntermediateStages; - private readonly PacketPipelineStage<TIntermediate, TOutput> pipelineOutputStage; - - internal PacketPipeline( - PacketPipelineStage<TInput, TIntermediate> firstStage, - PacketPipelineStage<TIntermediate, TOutput> lastStage, - IReadOnlyCollection<PacketPipelineStage<TIntermediate, TIntermediate>> intermediateStages) - { - pipelineInputStage = firstStage; - pipelineOutputStage = lastStage; - - pipelineIntermediateStages = intermediateStages; - } - - /// <summary> - /// Passes the given packet through the pipeline. - /// </summary> - /// <param name="inputPacket">The incoming packet.</param> - /// <returns>The outgoing transformed packet.</returns> - internal TOutput ProcessPacket(TInput inputPacket) - { - TIntermediate intermediatePacket = pipelineInputStage.Process(inputPacket); - - intermediatePacket = pipelineIntermediateStages.Aggregate(intermediatePacket, (current, stage) => stage.Process(current)); - - return pipelineOutputStage.Process(intermediatePacket); - } - } - - /// <summary> - /// Represents a single transformation applied to a packet traveling through the pipeline. - /// </summary> - /// <typeparam name="TInput">The type the transformation takes as input.</typeparam> - /// <typeparam name="TOutput">The type the transformation produces as output.</typeparam> - internal readonly struct PacketPipelineStage<TInput, TOutput> - { - private readonly Func<TInput, TOutput> stageDelegate; - - internal PacketPipelineStage(in Func<TInput, TOutput> stageProcessingDelegate) - { - stageDelegate = stageProcessingDelegate; - } - - internal TOutput Process(TInput input) - { - return stageDelegate(input); - } - } -} -\ No newline at end of file diff --git a/NetSharp/NetSharp/Pipelines/PacketPipelineBuilder.cs b/NetSharp/NetSharp/Pipelines/PacketPipelineBuilder.cs @@ -1,87 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace NetSharp.Pipelines -{ - /// <summary> - /// Allows for configuring and subsequently building a <see cref="PacketPipeline{TInput,TIntermediate,TOutput}"/> instance. - /// </summary> - /// <typeparam name="TInput">The type of packet that will be submitted to the pipeline.</typeparam> - /// <typeparam name="TIntermediate">The type of packet that will be handled internally by the pipeline.</typeparam> - /// <typeparam name="TOutput">The type of packet that will be requested from the pipeline.</typeparam> - internal sealed class PacketPipelineBuilder<TInput, TIntermediate, TOutput> - { - private readonly List<PacketPipelineStage<TIntermediate, TIntermediate>> intermediateStages; - - private PacketPipelineStage<TInput, TIntermediate>? inputStage; - private PacketPipelineStage<TIntermediate, TOutput>? outputStage; - - internal PacketPipelineBuilder() - { - intermediateStages = new List<PacketPipelineStage<TIntermediate, TIntermediate>>(); - } - - /// <summary> - /// Returns the currently configured <see cref="PacketPipeline{TInput,TIntermediate,TOutput}"/> instance. - /// </summary> - /// <returns>The configured <see cref="PacketPipeline{TInput,TIntermediate,TOutput}"/> instance.</returns> - /// <exception cref="ArgumentNullException"> - /// Thrown when either <see cref="WithInputStage"/> or <see cref="WithOutputStage"/> have not been called. - /// </exception> - internal PacketPipeline<TInput, TIntermediate, TOutput> Build() - { - if (inputStage == null) - { - throw new ArgumentNullException(nameof(inputStage), $"{nameof(WithInputStage)} has not been called."); - } - - if (outputStage == null) - { - throw new ArgumentNullException(nameof(outputStage), $"{nameof(WithOutputStage)} has not been called."); - } - - return new PacketPipeline<TInput, TIntermediate, TOutput>(inputStage.Value, outputStage.Value, intermediateStages); - } - - /// <summary> - /// Configures the input stage for the pipeline. - /// </summary> - /// <param name="stage"> - /// The transformation that should be applied to incoming packets, to convert them from the <typeparamref name="TInput"/> - /// type to the <typeparamref name="TIntermediate"/> type that the pipeline handles internally. - /// </param> - /// <returns>The builder instance for further configuration.</returns> - internal PacketPipelineBuilder<TInput, TIntermediate, TOutput> WithInputStage(in Func<TInput, TIntermediate> stage) - { - inputStage = new PacketPipelineStage<TInput, TIntermediate>(in stage); - return this; - } - - /// <summary> - /// Adds the given intermediate stage to the pipeline. - /// </summary> - /// <param name="stage"> - /// The transformation that should be applied to packets traveling through the pipeline. - /// </param> - /// <returns>The builder instance for further configuration.</returns> - internal PacketPipelineBuilder<TInput, TIntermediate, TOutput> WithIntermediateStage(in Func<TIntermediate, TIntermediate> stage) - { - intermediateStages.Add(new PacketPipelineStage<TIntermediate, TIntermediate>(in stage)); - return this; - } - - /// <summary> - /// Configures the output stage for the pipeline. - /// </summary> - /// <param name="stage"> - /// The transformation that should be applied to outgoing packets, to convert them from the - /// <typeparamref name="TIntermediate"/> type used internally to the <typeparamref name="TOutput"/> type. - /// </param> - /// <returns>The builder instance for further configuration.</returns> - internal PacketPipelineBuilder<TInput, TIntermediate, TOutput> WithOutputStage(in Func<TIntermediate, TOutput> stage) - { - outputStage = new PacketPipelineStage<TIntermediate, TOutput>(in stage); - return this; - } - } -} -\ No newline at end of file diff --git a/NetSharp/NetSharp/Sockets/Datagram/DatagramSocketClient.cs b/NetSharp/NetSharp/Sockets/Datagram/DatagramSocketClient.cs @@ -0,0 +1,12 @@ +using System.Net.Sockets; + +namespace NetSharp.Sockets.Datagram +{ + public class DatagramSocketClient : SocketClient + { + public DatagramSocketClient(in AddressFamily connectionAddressFamily, in ProtocolType connectionProtocolType) + : base(in connectionAddressFamily, SocketType.Dgram, in connectionProtocolType) + { + } + } +} +\ No newline at end of file diff --git a/NetSharp/NetSharp/Sockets/Datagram/DatagramSocketServer.cs b/NetSharp/NetSharp/Sockets/Datagram/DatagramSocketServer.cs @@ -0,0 +1,169 @@ +using NetSharp.Packets; +using NetSharp.Utils; + +using System; +using System.Collections.Concurrent; +using System.Net; +using System.Net.Sockets; +using System.Text; +using System.Threading; +using System.Threading.Channels; +using System.Threading.Tasks; + +namespace NetSharp.Sockets.Datagram +{ + public class DatagramSocketServer : SocketServer + { + private readonly ConcurrentDictionary<EndPoint, RemoteDatagramClientToken> connectedClientTokens; + + private readonly struct RemoteDatagramClientToken + { + private readonly Channel<NetworkPacket> PacketChannel; + + public readonly ChannelReader<NetworkPacket> PacketReader; + + public readonly ChannelWriter<NetworkPacket> PacketWriter; + + public RemoteDatagramClientToken(in Channel<NetworkPacket> packetChannel) + { + PacketChannel = packetChannel; + PacketReader = packetChannel.Reader; + PacketWriter = packetChannel.Writer; + } + } + + public DatagramSocketServer(in AddressFamily connectionAddressFamily, in ProtocolType connectionProtocolType) + : base(in connectionAddressFamily, SocketType.Dgram, in connectionProtocolType) + { + connectedClientTokens = new ConcurrentDictionary<EndPoint, RemoteDatagramClientToken>(); + } + + protected override SocketAsyncEventArgs GenerateConnectionArgs(EndPoint remoteEndPoint) + { + SocketAsyncEventArgs connectionArgs = new SocketAsyncEventArgs { RemoteEndPoint = remoteEndPoint }; + + connectionArgs.Completed += SocketAsyncOperations.HandleIoCompleted; + + return connectionArgs; + } + + protected override void DestroyConnectionArgs(SocketAsyncEventArgs remoteConnectionArgs) + { + remoteConnectionArgs.Completed -= SocketAsyncOperations.HandleIoCompleted; + + remoteConnectionArgs.Dispose(); + } + + protected override async Task HandleClient(SocketAsyncEventArgs clientArgs, CancellationToken cancellationToken = default) + { + EndPoint clientEndPoint = clientArgs.RemoteEndPoint; + RemoteDatagramClientToken clientToken = connectedClientTokens[clientEndPoint]; + + byte[] responseBuffer = new byte[NetworkPacket.TotalSize]; + Memory<byte> responseBufferMemory = new Memory<byte>(responseBuffer); + + try + { + while (!cancellationToken.IsCancellationRequested) + { + NetworkPacket request = await clientToken.PacketReader.ReadAsync(cancellationToken); + + // TODO implement actual request handling, besides just an echo + NetworkPacket response = request; + + NetworkPacket.Serialise(response, responseBufferMemory); + + TransmissionResult sendResult = + await SocketAsyncOperations + .SendToAsync(clientArgs, connection, clientEndPoint, SocketFlags.None, responseBufferMemory, cancellationToken); + +#if DEBUG + lock (typeof(Console)) + { + Console.WriteLine($"[Server] Sent {sendResult.Count} bytes to {sendResult.RemoteEndPoint}"); + Console.WriteLine($"[Server] >>>> {Encoding.UTF8.GetString(sendResult.Buffer.Span)}"); + } +#endif + } + } + catch (OperationCanceledException) { } + finally + { + DestroyConnectionArgs(clientArgs); + } + } + + public override async Task RunAsync(CancellationToken cancellationToken = default) + { + byte[] requestBuffer = new byte[NetworkPacket.TotalSize]; + Memory<byte> requestBufferMemory = new Memory<byte>(requestBuffer); + + EndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0); + using SocketAsyncEventArgs remoteArgs = GenerateConnectionArgs(remoteEndPoint); + + while (!cancellationToken.IsCancellationRequested) + { + remoteArgs.RemoteEndPoint = remoteEndPoint; + + TransmissionResult receiveResult = + await SocketAsyncOperations + .ReceiveFromAsync(remoteArgs, connection, remoteEndPoint, SocketFlags.None, requestBufferMemory, cancellationToken) + .ConfigureAwait(false); + + EndPoint clientEndPoint = receiveResult.RemoteEndPoint; + +#if DEBUG + lock (typeof(Console)) + { + Console.WriteLine($"[Server] Received {receiveResult.Count} bytes from {receiveResult.RemoteEndPoint}"); + Console.WriteLine($"[Server] <<<< {Encoding.UTF8.GetString(receiveResult.Buffer.Span)}"); + } +#endif + + if (!ConnectedClientHandlerTasks.ContainsKey(clientEndPoint)) + { + SocketAsyncEventArgs clientArgs = GenerateConnectionArgs(receiveResult.RemoteEndPoint); + + BoundedChannelOptions clientChannelOptions = new BoundedChannelOptions(60) + { FullMode = BoundedChannelFullMode.DropOldest, SingleReader = true, SingleWriter = true }; + Channel<NetworkPacket> clientChannel = Channel.CreateBounded<NetworkPacket>(clientChannelOptions); + + connectedClientTokens[clientEndPoint] = new RemoteDatagramClientToken(in clientChannel); + + ConnectedClientHandlerTasks[clientEndPoint] = HandleClient(clientArgs, cancellationToken); + } + + NetworkPacket requestPacket = NetworkPacket.Deserialise(requestBufferMemory); + + await connectedClientTokens[clientEndPoint].PacketWriter.WriteAsync(requestPacket, cancellationToken); + } + + /* + while (true) + { + TransmissionResult receiveResult = + await SocketAsyncEventArgs.ReceiveAsync(remoteEndPoint, SocketFlags.None, requestBufferMemory); + +#if DEBUG + lock (typeof(Console)) + { + Console.WriteLine($"[Server] Received {receiveResult.Count} bytes from {receiveResult.RemoteEndPoint}"); + Console.WriteLine($"[Server] <<<< {Encoding.UTF8.GetString(receiveResult.Buffer.Span)}"); + } +#endif + + TransmissionResult sendResult = + await SocketAsyncOperations.SendAsync(receiveResult.RemoteEndPoint, SocketFlags.None, requestBuffer); + +#if DEBUG + lock (typeof(Console)) + { + Console.WriteLine($"[Server] Sent {sendResult.Count} bytes to {sendResult.RemoteEndPoint}"); + Console.WriteLine($"[Server] >>>> {Encoding.UTF8.GetString(sendResult.Buffer.Span)}"); + } +#endif + } + */ + } + } +} +\ No newline at end of file diff --git a/NetSharp/NetSharp/Sockets/SocketAcceptor.cs b/NetSharp/NetSharp/Sockets/SocketAcceptor.cs @@ -1,280 +0,0 @@ -using System; -using System.Net; -using System.Net.Sockets; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Extensions.ObjectPool; - -namespace NetSharp.Sockets -{ - /// <summary> - /// Helper class providing awaitable wrappers around asynchronous Accept, Connect, and Disconnect operations. - /// </summary> - public sealed class SocketAcceptor - { - private readonly ObjectPool<SocketAsyncEventArgs> acceptAsyncEventArgsPool; - private readonly ObjectPool<SocketAsyncEventArgs> connectAsyncEventArgsPool; - private readonly ObjectPool<SocketAsyncEventArgs> disconnectAsyncEventArgsPool; - - private void HandleIOCompleted(object? sender, SocketAsyncEventArgs args) - { - switch (args.LastOperation) - { - case SocketAsyncOperation.Accept: - AsyncAcceptToken asyncAcceptToken = (AsyncAcceptToken)args.UserToken; - - if (asyncAcceptToken.CancellationToken.IsCancellationRequested) - { - asyncAcceptToken.CompletionSource.SetCanceled(); - } - else - { - if (args.SocketError != SocketError.Success) - { - asyncAcceptToken.CompletionSource.SetException( - new SocketException((int)args.SocketError)); - } - else - { - asyncAcceptToken.CompletionSource.SetResult(args.AcceptSocket); - } - } - - acceptAsyncEventArgsPool.Return(args); - - break; - - case SocketAsyncOperation.Connect: - AsyncConnectToken asyncConnectToken = (AsyncConnectToken)args.UserToken; - - if (asyncConnectToken.CancellationToken.IsCancellationRequested) - { - asyncConnectToken.CompletionSource.SetCanceled(); - } - else - { - if (args.SocketError != SocketError.Success) - { - asyncConnectToken.CompletionSource.SetException( - new SocketException((int)args.SocketError)); - } - else - { - asyncConnectToken.CompletionSource.SetResult(true); - } - } - - connectAsyncEventArgsPool.Return(args); - - break; - - case SocketAsyncOperation.Disconnect: - AsyncDisconnectToken asyncDisconnectToken = (AsyncDisconnectToken)args.UserToken; - - if (asyncDisconnectToken.CancellationToken.IsCancellationRequested) - { - asyncDisconnectToken.CompletionSource.SetCanceled(); - } - else - { - if (args.SocketError != SocketError.Success) - { - asyncDisconnectToken.CompletionSource.SetException( - new SocketException((int)args.SocketError)); - } - else - { - asyncDisconnectToken.CompletionSource.SetResult(true); - } - } - - disconnectAsyncEventArgsPool.Return(args); - - break; - - default: - throw new InvalidOperationException( - $"The {nameof(SocketAcceptor)} class doesn't support the {args.LastOperation} operation."); - } - } - - private readonly struct AsyncAcceptToken - { - public readonly CancellationToken CancellationToken; - public readonly TaskCompletionSource<Socket> CompletionSource; - - public AsyncAcceptToken(TaskCompletionSource<Socket> tcs, CancellationToken cancellationToken = default) - { - CompletionSource = tcs; - CancellationToken = cancellationToken; - } - } - - private readonly struct AsyncConnectToken - { - public readonly CancellationToken CancellationToken; - public readonly TaskCompletionSource<bool> CompletionSource; - - public AsyncConnectToken(TaskCompletionSource<bool> tcs, CancellationToken cancellationToken = default) - { - CompletionSource = tcs; - CancellationToken = cancellationToken; - } - } - - private readonly struct AsyncDisconnectToken - { - public readonly CancellationToken CancellationToken; - public readonly TaskCompletionSource<bool> CompletionSource; - - public AsyncDisconnectToken(TaskCompletionSource<bool> tcs, CancellationToken cancellationToken = default) - { - CompletionSource = tcs; - CancellationToken = cancellationToken; - } - } - - internal SocketAcceptor(int maxPooledObjects = 10) - { - acceptAsyncEventArgsPool = - new DefaultObjectPool<SocketAsyncEventArgs>(new DefaultPooledObjectPolicy<SocketAsyncEventArgs>(), - maxPooledObjects); - - connectAsyncEventArgsPool = - new DefaultObjectPool<SocketAsyncEventArgs>(new DefaultPooledObjectPolicy<SocketAsyncEventArgs>(), - maxPooledObjects); - - disconnectAsyncEventArgsPool = - new DefaultObjectPool<SocketAsyncEventArgs>(new DefaultPooledObjectPolicy<SocketAsyncEventArgs>(), - maxPooledObjects); - - for (int i = 0; i < maxPooledObjects; i++) - { - SocketAsyncEventArgs acceptArgs = new SocketAsyncEventArgs(); - acceptArgs.Completed += HandleIOCompleted; - acceptAsyncEventArgsPool.Return(acceptArgs); - - SocketAsyncEventArgs connectArgs = new SocketAsyncEventArgs(); - connectArgs.Completed += HandleIOCompleted; - connectAsyncEventArgsPool.Return(connectArgs); - - SocketAsyncEventArgs disconnectArgs = new SocketAsyncEventArgs(); - disconnectArgs.Completed += HandleIOCompleted; - connectAsyncEventArgsPool.Return(disconnectArgs); - } - } - - /// <summary> - /// Provides an awaitable wrapper around an asynchronous socket accept operation. - /// </summary> - /// <param name="socket">The socket which should be used to accept an incoming connection attempt.</param> - /// <param name="cancellationToken">The cancellation token to observe for the operation.</param> - /// <returns>The accepted socket.</returns> - public Task<Socket> AcceptAsync(Socket socket, CancellationToken cancellationToken = default) - { - TaskCompletionSource<Socket> tcs = new TaskCompletionSource<Socket>(); - - SocketAsyncEventArgs args = acceptAsyncEventArgsPool.Get(); - args.UserToken = new AsyncAcceptToken(tcs, cancellationToken); - - /* - // register cleanup action for when the cancellation token is thrown - cancellationToken.Register(() => - { - tcs.SetCanceled(); - - //TODO this is probably a hideous solution. find a better one - args.Completed -= HandleIOCompleted; - args.Dispose(); - - SocketAsyncEventArgs newArgs = new SocketAsyncEventArgs(); - newArgs.Completed += HandleIOCompleted; - acceptAsyncEventArgsPool.Return(newArgs); - }); - */ - - // if the accept operation doesn't complete synchronously, return the awaitable task - if (socket.AcceptAsync(args)) return tcs.Task; - - Socket result = args.AcceptSocket; - - acceptAsyncEventArgsPool.Return(args); - - return Task.FromResult(result); - } - - /// <summary> - /// Provides an awaitable wrapper around an asynchronous socket connect operation. - /// </summary> - /// <param name="socket">The socket which should asynchronously connect to the remote endpoint.</param> - /// <param name="remoteEndPoint">The remote endpoint to which the socket should connect.</param> - /// <param name="cancellationToken">The cancellation token to observe for the operation.</param> - public Task ConnectAsync(Socket socket, EndPoint remoteEndPoint, CancellationToken cancellationToken = default) - { - TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>(); - - SocketAsyncEventArgs args = connectAsyncEventArgsPool.Get(); - args.RemoteEndPoint = remoteEndPoint; - args.UserToken = new AsyncConnectToken(tcs, cancellationToken); - - /* - // register cleanup action for when the cancellation token is thrown - cancellationToken.Register(() => - { - tcs.SetCanceled(); - - //TODO this is probably a hideous solution. find a better one - args.Completed -= HandleIOCompleted; - args.Dispose(); - - SocketAsyncEventArgs newArgs = new SocketAsyncEventArgs(); - newArgs.Completed += HandleIOCompleted; - connectAsyncEventArgsPool.Return(newArgs); - }); - */ - - // if the connect operation doesn't complete synchronously, return the awaitable task - if (socket.ConnectAsync(args)) return tcs.Task; - - connectAsyncEventArgsPool.Return(args); - - return Task.CompletedTask; - } - - /// <summary> - /// Provides an awaitable wrapper around an asynchronous socket disconnect operation. - /// </summary> - /// <param name="socket">The socket which should asynchronously disconnect from its remote endpoint.</param> - /// <param name="cancellationToken">The cancellation token to observe for the operation.</param> - public Task DisconnectAsync(Socket socket, CancellationToken cancellationToken = default) - { - TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>(); - - SocketAsyncEventArgs args = connectAsyncEventArgsPool.Get(); - args.UserToken = new AsyncDisconnectToken(tcs, cancellationToken); - - /* - // register cleanup action for when the cancellation token is thrown - cancellationToken.Register(() => - { - tcs.SetCanceled(); - - //TODO this is probably a hideous solution. find a better one - args.Completed -= HandleIOCompleted; - args.Dispose(); - - SocketAsyncEventArgs newArgs = new SocketAsyncEventArgs(); - newArgs.Completed += HandleIOCompleted; - disconnectAsyncEventArgsPool.Return(newArgs); - }); - */ - - // if the disconnect operation doesn't complete synchronously, return the awaitable task - if (socket.DisconnectAsync(args)) return tcs.Task; - - connectAsyncEventArgsPool.Return(args); - - return Task.CompletedTask; - } - } -} -\ No newline at end of file diff --git a/NetSharp/NetSharp/Sockets/SocketAsyncOperationTokens.cs b/NetSharp/NetSharp/Sockets/SocketAsyncOperationTokens.cs @@ -0,0 +1,91 @@ +using NetSharp.Utils; + +using System.Threading; +using System.Threading.Tasks; + +namespace NetSharp.Sockets +{ + internal readonly struct AsyncAcceptToken + { + public readonly CancellationToken CancellationToken; + public readonly TaskCompletionSource<bool> CompletionSource; + + public AsyncAcceptToken(in TaskCompletionSource<bool> tcs, in CancellationToken cancellationToken = default) + { + CompletionSource = tcs; + CancellationToken = cancellationToken; + } + } + + internal readonly struct AsyncConnectToken + { + public readonly CancellationToken CancellationToken; + public readonly TaskCompletionSource<bool> CompletionSource; + + public AsyncConnectToken(in TaskCompletionSource<bool> tcs, in CancellationToken cancellationToken = default) + { + CompletionSource = tcs; + CancellationToken = cancellationToken; + } + } + + internal readonly struct AsyncDisconnectToken + { + public readonly CancellationToken CancellationToken; + public readonly TaskCompletionSource<bool> CompletionSource; + + public AsyncDisconnectToken(in TaskCompletionSource<bool> tcs, in CancellationToken cancellationToken = default) + { + CompletionSource = tcs; + CancellationToken = cancellationToken; + } + } + + internal readonly struct AsyncReadToken + { + public readonly CancellationToken CancellationToken; + public readonly TaskCompletionSource<TransmissionResult> CompletionSource; + + public AsyncReadToken(in TaskCompletionSource<TransmissionResult> tcs, in CancellationToken cancellationToken = default) + { + CompletionSource = tcs; + CancellationToken = cancellationToken; + } + } + + internal readonly struct AsyncWriteToken + { + public readonly CancellationToken CancellationToken; + public readonly TaskCompletionSource<TransmissionResult> CompletionSource; + + public AsyncWriteToken(in TaskCompletionSource<TransmissionResult> tcs, in CancellationToken cancellationToken = default) + { + CompletionSource = tcs; + CancellationToken = cancellationToken; + } + } + + internal readonly struct AsyncReadFromToken + { + public readonly CancellationToken CancellationToken; + public readonly TaskCompletionSource<TransmissionResult> CompletionSource; + + public AsyncReadFromToken(in TaskCompletionSource<TransmissionResult> tcs, in CancellationToken cancellationToken = default) + { + CompletionSource = tcs; + CancellationToken = cancellationToken; + } + } + + internal readonly struct AsyncWriteToToken + { + public readonly CancellationToken CancellationToken; + public readonly TaskCompletionSource<TransmissionResult> CompletionSource; + + public AsyncWriteToToken(in TaskCompletionSource<TransmissionResult> tcs, in CancellationToken cancellationToken = default) + { + CompletionSource = tcs; + CancellationToken = cancellationToken; + } + } +} +\ No newline at end of file diff --git a/NetSharp/NetSharp/Sockets/SocketAsyncOperations.cs b/NetSharp/NetSharp/Sockets/SocketAsyncOperations.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 +{ + public static class SocketAsyncOperations + { + /// <summary> + /// Event handler for the <see cref="SocketAsyncEventArgs.Completed"/> event. + /// </summary> + /// <param name="sender">The object on which the event is raised.</param> + /// <param name="args">The event arguments.</param> + public static void HandleIoCompleted(object sender, SocketAsyncEventArgs args) + { + switch (args.LastOperation) + { + case SocketAsyncOperation.Accept: + AsyncAcceptToken asyncAcceptToken = (AsyncAcceptToken)args.UserToken; + + if (asyncAcceptToken.CancellationToken.IsCancellationRequested) + { + asyncAcceptToken.CompletionSource.SetCanceled(); + } + else + { + if (args.SocketError != SocketError.Success) + { + asyncAcceptToken.CompletionSource.SetException( + new SocketException((int)args.SocketError)); + } + else + { + asyncAcceptToken.CompletionSource.SetResult(true); + } + } + + break; + + case SocketAsyncOperation.Connect: + AsyncConnectToken asyncConnectToken = (AsyncConnectToken)args.UserToken; + + if (asyncConnectToken.CancellationToken.IsCancellationRequested) + { + asyncConnectToken.CompletionSource.SetCanceled(); + } + else + { + if (args.SocketError != SocketError.Success) + { + asyncConnectToken.CompletionSource.SetException( + new SocketException((int)args.SocketError)); + } + else + { + asyncConnectToken.CompletionSource.SetResult(true); + } + } + + break; + + case SocketAsyncOperation.Disconnect: + AsyncDisconnectToken asyncDisconnectToken = (AsyncDisconnectToken)args.UserToken; + + if (asyncDisconnectToken.CancellationToken.IsCancellationRequested) + { + asyncDisconnectToken.CompletionSource.SetCanceled(); + } + else + { + if (args.SocketError != SocketError.Success) + { + asyncDisconnectToken.CompletionSource.SetException( + new SocketException((int)args.SocketError)); + } + else + { + asyncDisconnectToken.CompletionSource.SetResult(true); + } + } + + break; + + case SocketAsyncOperation.Receive: + AsyncReadToken asyncReceiveToken = (AsyncReadToken)args.UserToken; + + if (asyncReceiveToken.CancellationToken.IsCancellationRequested) + { + asyncReceiveToken.CompletionSource.SetCanceled(); + } + else + { + if (args.SocketError != SocketError.Success) + { + asyncReceiveToken.CompletionSource.SetException( + new SocketException((int)args.SocketError)); + } + else if (args.BytesTransferred > 0) + { + TransmissionResult result = new TransmissionResult(args); + + asyncReceiveToken.CompletionSource.SetResult(result); + } + else + { + asyncReceiveToken.CompletionSource.SetException( + new Exception($"Receive method received 0 bytes from remote endpoint!")); + } + } + + break; + + case SocketAsyncOperation.ReceiveFrom: + AsyncReadFromToken asyncReceiveFromToken = (AsyncReadFromToken)args.UserToken; + + if (asyncReceiveFromToken.CancellationToken.IsCancellationRequested) + { + asyncReceiveFromToken.CompletionSource.SetCanceled(); + } + else + { + if (args.SocketError != SocketError.Success) + { + asyncReceiveFromToken.CompletionSource.SetException( + new SocketException((int)args.SocketError)); + } + else + { + TransmissionResult result = new TransmissionResult(args); + + asyncReceiveFromToken.CompletionSource.SetResult(result); + } + } + + break; + + case SocketAsyncOperation.Send: + AsyncWriteToken asyncSendToken = (AsyncWriteToken)args.UserToken; + + if (asyncSendToken.CancellationToken.IsCancellationRequested) + { + asyncSendToken.CompletionSource.SetCanceled(); + } + else + { + if (args.SocketError != SocketError.Success) + { + asyncSendToken.CompletionSource.SetException( + new SocketException((int)args.SocketError)); + } + else + { + TransmissionResult result = new TransmissionResult(args); + + asyncSendToken.CompletionSource.SetResult(result); + } + } + + break; + + case SocketAsyncOperation.SendTo: + AsyncWriteToToken asyncSendToToken = (AsyncWriteToToken)args.UserToken; + + if (asyncSendToToken.CancellationToken.IsCancellationRequested) + { + asyncSendToToken.CompletionSource.SetCanceled(); + } + else + { + if (args.SocketError != SocketError.Success) + { + asyncSendToToken.CompletionSource.SetException( + new SocketException((int)args.SocketError)); + } + else + { + TransmissionResult result = new TransmissionResult(args); + + asyncSendToToken.CompletionSource.SetResult(result); + } + } + + break; + + case SocketAsyncOperation.None: + case SocketAsyncOperation.ReceiveMessageFrom: + case SocketAsyncOperation.SendPackets: + throw new InvalidOperationException( + $"{nameof(args.LastOperation)} is not supported by {nameof(SocketAsyncOperations)}"); + + default: + throw new ArgumentOutOfRangeException(nameof(args.LastOperation), + $"Invalid value in the {nameof(args.LastOperation)} enum."); + } + } + + public static ValueTask AcceptAsync(SocketAsyncEventArgs clientAcceptArgs, Socket socket, + CancellationToken cancellationToken = default) + { + TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>(); + + clientAcceptArgs.UserToken = new AsyncAcceptToken(tcs, cancellationToken); + + // if the accept operation doesn't complete synchronously, return the awaitable task + return socket.AcceptAsync(clientAcceptArgs) ? new ValueTask(tcs.Task) : new ValueTask(); + } + + public static ValueTask ConnectAsync(SocketAsyncEventArgs clientConnectArgs, Socket socket, EndPoint remoteEndPoint, + CancellationToken cancellationToken = default) + { + TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>(); + + clientConnectArgs.RemoteEndPoint = remoteEndPoint; + clientConnectArgs.UserToken = new AsyncConnectToken(tcs, cancellationToken); + + // if the connect operation doesn't complete synchronously, return the awaitable task + return socket.ConnectAsync(clientConnectArgs) ? new ValueTask(tcs.Task) : new ValueTask(); + } + + public static ValueTask DisconnectAsync(SocketAsyncEventArgs clientDisconnectArgs, Socket socket, + CancellationToken cancellationToken = default) + { + TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>(); + + clientDisconnectArgs.UserToken = new AsyncDisconnectToken(tcs, cancellationToken); + + // if the disconnect operation doesn't complete synchronously, return the awaitable task + return socket.DisconnectAsync(clientDisconnectArgs) ? new ValueTask(tcs.Task) : new ValueTask(); + } + + public static ValueTask<TransmissionResult> ReceiveAsync(SocketAsyncEventArgs socketArgs, Socket socket, EndPoint remoteEndPoint, + SocketFlags socketFlags, Memory<byte> inputBuffer, CancellationToken cancellationToken = default) + { + TaskCompletionSource<TransmissionResult> tcs = new TaskCompletionSource<TransmissionResult>(); + + socketArgs.SetBuffer(inputBuffer); + socketArgs.SocketFlags = socketFlags; + socketArgs.RemoteEndPoint = remoteEndPoint; + socketArgs.UserToken = new AsyncReadToken(tcs, cancellationToken); + + // if the receive operation doesn't complete synchronously, returns the awaitable task + if (socket.ReceiveAsync(socketArgs)) return new ValueTask<TransmissionResult>(tcs.Task); + + TransmissionResult result = new TransmissionResult(socketArgs); + + return new ValueTask<TransmissionResult>(result); + } + + public static ValueTask<TransmissionResult> ReceiveFromAsync(SocketAsyncEventArgs socketArgs, Socket socket, EndPoint remoteEndPoint, + SocketFlags socketFlags, Memory<byte> inputBuffer, CancellationToken cancellationToken = default) + { + TaskCompletionSource<TransmissionResult> tcs = new TaskCompletionSource<TransmissionResult>(); + + socketArgs.SetBuffer(inputBuffer); + socketArgs.SocketFlags = socketFlags; + socketArgs.RemoteEndPoint = remoteEndPoint; + socketArgs.UserToken = new AsyncReadFromToken(tcs, cancellationToken); + + // if the receive operation doesn't complete synchronously, returns the awaitable task + if (socket.ReceiveFromAsync(socketArgs)) return new ValueTask<TransmissionResult>(tcs.Task); + + TransmissionResult result = new TransmissionResult(socketArgs); + + return new ValueTask<TransmissionResult>(result); + } + + public static ValueTask<TransmissionResult> SendAsync(SocketAsyncEventArgs socketArgs, Socket socket, EndPoint remoteEndPoint, + SocketFlags socketFlags, Memory<byte> outputBuffer, CancellationToken cancellationToken = default) + { + TaskCompletionSource<TransmissionResult> tcs = new TaskCompletionSource<TransmissionResult>(); + + socketArgs.SetBuffer(outputBuffer); + socketArgs.SocketFlags = socketFlags; + socketArgs.RemoteEndPoint = remoteEndPoint; + socketArgs.UserToken = new AsyncWriteToken(tcs, cancellationToken); + + // if the send operation doesn't complete synchronously, return the awaitable task + if (socket.SendAsync(socketArgs)) return new ValueTask<TransmissionResult>(tcs.Task); + + TransmissionResult result = new TransmissionResult(socketArgs); + + return new ValueTask<TransmissionResult>(result); + } + + public static ValueTask<TransmissionResult> SendToAsync(SocketAsyncEventArgs socketArgs, Socket socket, EndPoint remoteEndPoint, + SocketFlags socketFlags, Memory<byte> outputBuffer, CancellationToken cancellationToken = default) + { + TaskCompletionSource<TransmissionResult> tcs = new TaskCompletionSource<TransmissionResult>(); + + socketArgs.SetBuffer(outputBuffer); + socketArgs.SocketFlags = socketFlags; + socketArgs.RemoteEndPoint = remoteEndPoint; + socketArgs.UserToken = new AsyncWriteToToken(tcs, cancellationToken); + + // if the send operation doesn't complete synchronously, return the awaitable task + if (socket.SendToAsync(socketArgs)) return new ValueTask<TransmissionResult>(tcs.Task); + + TransmissionResult result = new TransmissionResult(socketArgs); + + return new ValueTask<TransmissionResult>(result); + } + } +} +\ No newline at end of file diff --git a/NetSharp/NetSharp/Sockets/SocketClient.cs b/NetSharp/NetSharp/Sockets/SocketClient.cs @@ -1,92 +1,48 @@ -using System; +using NetSharp.Packets; + +using System; +using System.Buffers; using System.Net; using System.Net.Sockets; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Extensions.ObjectPool; -using NetSharp.Utils; namespace NetSharp.Sockets { - public class SocketClient : IDisposable + public abstract class SocketClient : SocketConnection { - private readonly ObjectPool<SocketAsyncEventArgs> transmissionArgsPool; + protected readonly ArrayPool<byte> BufferPool; - /// <summary> - /// Destroys a socket client instance. - /// </summary> - ~SocketClient() + protected SocketClient(in AddressFamily connectionAddressFamily, in SocketType connectionSocketType, in ProtocolType connectionProtocolType) + : base(in connectionAddressFamily, in connectionSocketType, in connectionProtocolType) { - Dispose(false); + BufferPool = ArrayPool<byte>.Create(NetworkPacket.TotalSize, 10); } - protected readonly Socket transmitterSocket; - - /// <summary> - /// Implementation of dispose pattern. - /// </summary> - /// <param name="disposing"> - /// Whether this method is being called by the object finalizer, or by the <see cref="Dispose()"/> method. - /// </param> - protected virtual void Dispose(bool disposing) + public int SendBytes(Memory<byte> outgoingDataBuffer) { - if (disposing) - { - transmitterSocket.Dispose(); - } + return connection.Send(outgoingDataBuffer.Span); } - public SocketClient(AddressFamily transmitterAddressFamily, SocketType transmitterSocketType, - ProtocolType transmitterProtocolType) + public int SendBytesTo(Memory<byte> outgoingDataBuffer, EndPoint remoteEndPoint) { - transmitterSocket = new Socket(transmitterAddressFamily, transmitterSocketType, transmitterProtocolType); - - transmissionArgsPool = new DefaultObjectPool<SocketAsyncEventArgs>(new DefaultPooledObjectPolicy<SocketAsyncEventArgs>()); + return connection.SendTo(outgoingDataBuffer.ToArray(), remoteEndPoint); } - /// <inheritdoc /> - public void Dispose() + public int ReceiveBytes(Memory<byte> incomingDataBuffer) { - Dispose(true); - GC.SuppressFinalize(this); - } + byte[] temporaryBuffer = new byte[NetworkPacket.TotalSize]; + int receivedBytes = connection.Receive(temporaryBuffer); + temporaryBuffer.CopyTo(incomingDataBuffer); - public ValueTask<TransmissionResult> ReceiveAsync(EndPoint remoteEndPoint, SocketFlags receiveFlags, Memory<byte> receiveBuffer, - CancellationToken cancellationToken = default) - { - return SocketOperations.ReceiveFromAsync(transmissionArgsPool, transmitterSocket, remoteEndPoint, - receiveFlags, receiveBuffer, cancellationToken); + return receivedBytes; } - public ValueTask<TransmissionResult> SendAsync(EndPoint remoteEndPoint, SocketFlags sendFlags, Memory<byte> sendBuffer, - CancellationToken cancellationToken = default) + public int ReceiveBytesFrom(Memory<byte> incomingDataBuffer, ref EndPoint remoteEndPoint) { - return SocketOperations.SendToAsync(transmissionArgsPool, transmitterSocket, remoteEndPoint, sendFlags, - sendBuffer, cancellationToken); - } - - public Task<bool> TryBindAsync(EndPoint localEndPoint, TimeSpan timeout) - { - using CancellationTokenSource cts = new CancellationTokenSource(timeout); - - try - { - return Task.Run(() => - { - transmitterSocket.Bind(localEndPoint); + byte[] temporaryBuffer = new byte[NetworkPacket.TotalSize]; + int receivedBytes = connection.ReceiveFrom(temporaryBuffer, ref remoteEndPoint); + temporaryBuffer.CopyTo(incomingDataBuffer); - return true; - }, cts.Token); - } - catch (TaskCanceledException) - { - return Task.FromResult(false); - } - catch (SocketException ex) - { - Console.WriteLine($"Socket exception on binding socket to {localEndPoint}: {ex}"); - return Task.FromResult(false); - } + return receivedBytes; } } } \ No newline at end of file diff --git a/NetSharp/NetSharp/Sockets/SocketConnection.cs b/NetSharp/NetSharp/Sockets/SocketConnection.cs @@ -0,0 +1,44 @@ +using System; +using System.Net; +using System.Net.Sockets; + +namespace NetSharp.Sockets +{ + public abstract class SocketConnection : IDisposable + { + protected readonly Socket connection; + + protected SocketConnection(in AddressFamily connectionAddressFamily, in SocketType connectionSocketType, in ProtocolType connectionProtocolType) + { + connection = new Socket(connectionAddressFamily, connectionSocketType, connectionProtocolType); + } + + public void Bind(in EndPoint localEndPoint) + { + connection.Bind(localEndPoint); + } + + public void Shutdown(SocketShutdown how) + { + try + { + connection.Shutdown(how); + } + catch (SocketException) { } + } + + protected virtual void Dispose(bool disposing) + { + if (!disposing) return; + + connection.Close(); + connection.Dispose(); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + } +} +\ No newline at end of file diff --git a/NetSharp/NetSharp/Sockets/SocketOperations.cs b/NetSharp/NetSharp/Sockets/SocketOperations.cs @@ -1,427 +0,0 @@ -using System; -using System.Buffers; -using System.Net; -using System.Net.Sockets; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Extensions.ObjectPool; -using NetSharp.Utils; - -namespace NetSharp.Sockets -{ - /// <summary> - /// Provides helper awaitable functions for wrapping the <see cref="SocketAsyncEventArgs"/> pattern. - /// </summary> - public static class SocketOperations - { - private static void HandleIOCompleted(object? sender, SocketAsyncEventArgs args) - { - args.Completed -= HandleIOCompleted; - - switch (args.LastOperation) - { - case SocketAsyncOperation.ReceiveFrom: - AsyncReadToken asyncReceiveFromToken = (AsyncReadToken)args.UserToken; - - if (asyncReceiveFromToken.CancellationToken.IsCancellationRequested) - { - asyncReceiveFromToken.CompletionSource.SetCanceled(); - } - else - { - if (args.SocketError != SocketError.Success) - { - asyncReceiveFromToken.CompletionSource.SetException( - new SocketException((int)args.SocketError)); - } - else - { - TransmissionResult result = new TransmissionResult(args); - - asyncReceiveFromToken.CompletionSource.SetResult(result); - } - } - - asyncReceiveFromToken.ReadArgsPool.Return(args); - - break; - - case SocketAsyncOperation.SendTo: - AsyncWriteToken asyncSendToToken = (AsyncWriteToken)args.UserToken; - - if (asyncSendToToken.CancellationToken.IsCancellationRequested) - { - asyncSendToToken.CompletionSource.SetCanceled(); - } - else - { - if (args.SocketError != SocketError.Success) - { - asyncSendToToken.CompletionSource.SetException( - new SocketException((int)args.SocketError)); - } - else - { - TransmissionResult result = new TransmissionResult(args); - - asyncSendToToken.CompletionSource.SetResult(result); - } - } - - asyncSendToToken.WriteArgsPool.Return(args); - break; - - case SocketAsyncOperation.Accept: - AsyncAcceptToken asyncAcceptToken = (AsyncAcceptToken)args.UserToken; - - if (asyncAcceptToken.CancellationToken.IsCancellationRequested) - { - asyncAcceptToken.CompletionSource.SetCanceled(); - } - else - { - if (args.SocketError != SocketError.Success) - { - asyncAcceptToken.CompletionSource.SetException( - new SocketException((int)args.SocketError)); - } - else - { - asyncAcceptToken.CompletionSource.SetResult(args.AcceptSocket); - } - } - - asyncAcceptToken.AcceptArgsPool.Return(args); - - break; - - case SocketAsyncOperation.Connect: - AsyncConnectToken asyncConnectToken = (AsyncConnectToken)args.UserToken; - - if (asyncConnectToken.CancellationToken.IsCancellationRequested) - { - asyncConnectToken.CompletionSource.SetCanceled(); - } - else - { - if (args.SocketError != SocketError.Success) - { - asyncConnectToken.CompletionSource.SetException( - new SocketException((int)args.SocketError)); - } - else - { - asyncConnectToken.CompletionSource.SetResult(args.ConnectSocket); - } - } - - asyncConnectToken.ConnectArgsPool.Return(args); - - break; - - case SocketAsyncOperation.Disconnect: - AsyncOperationToken asyncDisconnectToken = (AsyncOperationToken)args.UserToken; - - if (asyncDisconnectToken.CancellationToken.IsCancellationRequested) - { - asyncDisconnectToken.CompletionSource.SetCanceled(); - } - else - { - if (args.SocketError != SocketError.Success) - { - asyncDisconnectToken.CompletionSource.SetException( - new SocketException((int)args.SocketError)); - } - else - { - asyncDisconnectToken.CompletionSource.SetResult(true); - } - } - - asyncDisconnectToken.OperationArgsPool.Return(args); - - break; - - default: - throw new InvalidOperationException( - $"The {nameof(SocketReader)} class doesn't support the {args.LastOperation} operation."); - } - } - - private readonly struct AsyncAcceptToken - { - public readonly ObjectPool<SocketAsyncEventArgs> AcceptArgsPool; - public readonly CancellationToken CancellationToken; - public readonly TaskCompletionSource<Socket> CompletionSource; - - public AsyncAcceptToken(in ObjectPool<SocketAsyncEventArgs> argsPool, in TaskCompletionSource<Socket> tcs, - in CancellationToken cancellationToken = default) - { - AcceptArgsPool = argsPool; - - CompletionSource = tcs; - CancellationToken = cancellationToken; - } - } - - private readonly struct AsyncConnectToken - { - public readonly CancellationToken CancellationToken; - public readonly TaskCompletionSource<Socket> CompletionSource; - public readonly ObjectPool<SocketAsyncEventArgs> ConnectArgsPool; - - public AsyncConnectToken(in ObjectPool<SocketAsyncEventArgs> argsPool, in TaskCompletionSource<Socket> tcs, - in CancellationToken cancellationToken = default) - { - ConnectArgsPool = argsPool; - - CompletionSource = tcs; - CancellationToken = cancellationToken; - } - } - - private readonly struct AsyncOperationToken - { - public readonly CancellationToken CancellationToken; - public readonly TaskCompletionSource<bool> CompletionSource; - public readonly ObjectPool<SocketAsyncEventArgs> OperationArgsPool; - - public AsyncOperationToken(in ObjectPool<SocketAsyncEventArgs> argsPool, in TaskCompletionSource<bool> tcs, - in CancellationToken cancellationToken = default) - { - OperationArgsPool = argsPool; - - CompletionSource = tcs; - CancellationToken = cancellationToken; - } - } - - private readonly struct AsyncReadToken - { - public readonly CancellationToken CancellationToken; - public readonly TaskCompletionSource<TransmissionResult> CompletionSource; - public readonly ObjectPool<SocketAsyncEventArgs> ReadArgsPool; - public readonly Memory<byte> UserBuffer; - - public AsyncReadToken(in ObjectPool<SocketAsyncEventArgs> argsPool, in Memory<byte> userBuffer, - in TaskCompletionSource<TransmissionResult> tcs, in CancellationToken cancellationToken = default) - { - ReadArgsPool = argsPool; - UserBuffer = userBuffer; - - CompletionSource = tcs; - CancellationToken = cancellationToken; - } - } - - private readonly struct AsyncWriteToken - { - public readonly CancellationToken CancellationToken; - public readonly TaskCompletionSource<TransmissionResult> CompletionSource; - public readonly ObjectPool<SocketAsyncEventArgs> WriteArgsPool; - - public AsyncWriteToken(in ObjectPool<SocketAsyncEventArgs> argsPool, - in TaskCompletionSource<TransmissionResult> tcs, in CancellationToken cancellationToken = default) - { - WriteArgsPool = argsPool; - - CompletionSource = tcs; - CancellationToken = cancellationToken; - } - } - - public static Task<Socket> AcceptAsync(ObjectPool<SocketAsyncEventArgs> acceptArgsPool, - Socket socket, CancellationToken cancellationToken = default) - { - TaskCompletionSource<Socket> tcs = new TaskCompletionSource<Socket>(); - - SocketAsyncEventArgs args = acceptArgsPool.Get(); - args.UserToken = new AsyncAcceptToken(acceptArgsPool, tcs, cancellationToken); - - /* - // register cleanup action for when the cancellation token is thrown - cancellationToken.Register(() => - { - tcs.SetCanceled(); - - //TODO this is probably a hideous solution. find a better one - args.Completed -= HandleIOCompleted; - args.Dispose(); - - SocketAsyncEventArgs newArgs = new SocketAsyncEventArgs(); - newArgs.Completed += HandleIOCompleted; - acceptAsyncEventArgsPool.Return(newArgs); - }); - */ - - // if the accept operation doesn't complete synchronously, return the awaitable task - if (socket.AcceptAsync(args)) return tcs.Task; - - Socket result = args.AcceptSocket; - args.Completed -= HandleIOCompleted; - - acceptArgsPool.Return(args); - - return Task.FromResult(result); - } - - public static Task<Socket> ConnectAsync(ObjectPool<SocketAsyncEventArgs> connectArgsPool, - Socket socket, EndPoint remoteEndPoint, CancellationToken cancellationToken = default) - { - TaskCompletionSource<Socket> tcs = new TaskCompletionSource<Socket>(); - - SocketAsyncEventArgs args = connectArgsPool.Get(); - args.RemoteEndPoint = remoteEndPoint; - args.UserToken = new AsyncConnectToken(connectArgsPool, tcs, cancellationToken); - - /* - // register cleanup action for when the cancellation token is thrown - cancellationToken.Register(() => - { - tcs.SetCanceled(); - - //TODO this is probably a hideous solution. find a better one - args.Completed -= HandleIOCompleted; - args.Dispose(); - - SocketAsyncEventArgs newArgs = new SocketAsyncEventArgs(); - newArgs.Completed += HandleIOCompleted; - connectAsyncEventArgsPool.Return(newArgs); - }); - */ - - // if the connect operation doesn't complete synchronously, return the awaitable task - if (socket.ConnectAsync(args)) return tcs.Task; - - Socket result = args.ConnectSocket; - args.Completed -= HandleIOCompleted; - - connectArgsPool.Return(args); - - return Task.FromResult(result); - } - - public static Task DisconnectAsync(ObjectPool<SocketAsyncEventArgs> disconnectArgsPool, - Socket socket, CancellationToken cancellationToken = default) - { - TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>(); - - SocketAsyncEventArgs args = disconnectArgsPool.Get(); - args.UserToken = new AsyncOperationToken(disconnectArgsPool, tcs, cancellationToken); - - /* - // register cleanup action for when the cancellation token is thrown - cancellationToken.Register(() => - { - tcs.SetCanceled(); - - //TODO this is probably a hideous solution. find a better one - args.Completed -= HandleIOCompleted; - args.Dispose(); - - SocketAsyncEventArgs newArgs = new SocketAsyncEventArgs(); - newArgs.Completed += HandleIOCompleted; - disconnectAsyncEventArgsPool.Return(newArgs); - }); - */ - - // if the disconnect operation doesn't complete synchronously, return the awaitable task - if (socket.DisconnectAsync(args)) return tcs.Task; - - args.Completed -= HandleIOCompleted; - - disconnectArgsPool.Return(args); - - return Task.CompletedTask; - } - - public static ValueTask<TransmissionResult> ReceiveFromAsync(ObjectPool<SocketAsyncEventArgs> receiveArgsPool, - Socket socket, EndPoint remoteEndPoint, SocketFlags socketFlags, Memory<byte> inputBuffer, - CancellationToken cancellationToken = default) - { - TaskCompletionSource<TransmissionResult> tcs = new TaskCompletionSource<TransmissionResult>(); - - SocketAsyncEventArgs args = receiveArgsPool.Get(); - args.SetBuffer(inputBuffer); - args.SocketFlags = socketFlags; - args.RemoteEndPoint = remoteEndPoint; - args.UserToken = new AsyncReadToken(receiveArgsPool, inputBuffer, tcs, cancellationToken); - - args.Completed += HandleIOCompleted; - - /* - // register cleanup action for when the cancellation token is thrown - cancellationToken.Register(() => - { - tcs.SetCanceled(); - - receiveBufferPool.Return(rentedReceiveFromBuffer, true); - - //TODO this is probably a hideous solution. find a better one - args.Completed -= HandleIOCompleted; - args.Dispose(); - - SocketAsyncEventArgs newArgs = new SocketAsyncEventArgs(); - newArgs.Completed += HandleIOCompleted; - receiveAsyncEventArgsPool.Return(newArgs); - }); - */ - - // if the receive operation doesn't complete synchronously, returns the awaitable task - if (socket.ReceiveFromAsync(args)) return new ValueTask<TransmissionResult>(tcs.Task); - - args.Completed -= HandleIOCompleted; - - TransmissionResult result = new TransmissionResult(args); - - receiveArgsPool.Return(args); - - return new ValueTask<TransmissionResult>(result); - } - - public static ValueTask<TransmissionResult> SendToAsync(ObjectPool<SocketAsyncEventArgs> sendArgsPool, - Socket socket, EndPoint remoteEndPoint, SocketFlags socketFlags, Memory<byte> outputBuffer, - CancellationToken cancellationToken = default) - { - TaskCompletionSource<TransmissionResult> tcs = new TaskCompletionSource<TransmissionResult>(); - - SocketAsyncEventArgs args = sendArgsPool.Get(); - args.SetBuffer(outputBuffer); - args.SocketFlags = socketFlags; - args.RemoteEndPoint = remoteEndPoint; - args.UserToken = new AsyncWriteToken(sendArgsPool, tcs, cancellationToken); - - args.Completed += HandleIOCompleted; - - /* - // register cleanup action for when the cancellation token is thrown - cancellationToken.Register(() => - { - tcs.SetCanceled(); - - sendBufferPool.Return(rentedSendToBuffer, true); - - //TODO this is probably a hideous solution. find a better one - args.Completed -= HandleIOCompleted; - args.Dispose(); - - SocketAsyncEventArgs newArgs = new SocketAsyncEventArgs(); - newArgs.Completed += HandleIOCompleted; - sendAsyncEventArgsPool.Return(newArgs); - }); - */ - - // if the send operation doesn't complete synchronously, return the awaitable task - if (socket.SendToAsync(args)) return new ValueTask<TransmissionResult>(tcs.Task); - - args.Completed -= HandleIOCompleted; - - TransmissionResult result = new TransmissionResult(args); - - sendArgsPool.Return(args); - - return new ValueTask<TransmissionResult>(result); - } - } -} -\ No newline at end of file diff --git a/NetSharp/NetSharp/Sockets/SocketReader.cs b/NetSharp/NetSharp/Sockets/SocketReader.cs @@ -1,152 +0,0 @@ -using System; -using System.Buffers; -using System.Net; -using System.Net.Sockets; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Extensions.ObjectPool; -using NetSharp.Packets; -using NetSharp.Utils; - -namespace NetSharp.Sockets -{ - /// <summary> - /// Helper class providing awaitable wrappers around asynchronous Receive and ReceiveFrom operations. - /// </summary> - public sealed class SocketReader - { - private readonly int PacketBufferLength; - private readonly ObjectPool<SocketAsyncEventArgs> receiveFromAsyncEventArgsPool; - private readonly ArrayPool<byte> receiveFromBufferPool; - - private void HandleIOCompleted(object? sender, SocketAsyncEventArgs args) - { - switch (args.LastOperation) - { - case SocketAsyncOperation.ReceiveFrom: - AsyncReadToken asyncReceiveFromToken = (AsyncReadToken)args.UserToken; - - if (asyncReceiveFromToken.CancellationToken.IsCancellationRequested) - { - asyncReceiveFromToken.CompletionSource.SetCanceled(); - } - else - { - if (args.SocketError != SocketError.Success) - { - asyncReceiveFromToken.CompletionSource.SetException( - new SocketException((int)args.SocketError)); - } - else - { - args.MemoryBuffer.CopyTo(asyncReceiveFromToken.UserBuffer); - - TransmissionResult result = new TransmissionResult(args); - - asyncReceiveFromToken.CompletionSource.SetResult(result); - } - } - - receiveFromBufferPool.Return(asyncReceiveFromToken.RentedBuffer, true); - receiveFromAsyncEventArgsPool.Return(args); - - break; - - default: - throw new InvalidOperationException( - $"The {nameof(SocketReader)} class doesn't support the {args.LastOperation} operation."); - } - } - - private readonly struct AsyncReadToken - { - public readonly CancellationToken CancellationToken; - public readonly TaskCompletionSource<TransmissionResult> CompletionSource; - public readonly byte[] RentedBuffer; - public readonly Memory<byte> UserBuffer; - - public AsyncReadToken(byte[] rentedBuffer, Memory<byte> userBuffer, TaskCompletionSource<TransmissionResult> tcs, - CancellationToken cancellationToken = default) - { - RentedBuffer = rentedBuffer; - UserBuffer = userBuffer; - - CompletionSource = tcs; - CancellationToken = cancellationToken; - } - } - - internal SocketReader(int packetBufferLength = NetworkPacket.PacketSize, int maxPooledObjects = 10, - bool preallocateBuffers = false) - { - PacketBufferLength = packetBufferLength; - - receiveFromBufferPool = ArrayPool<byte>.Create(packetBufferLength, maxPooledObjects); - - receiveFromAsyncEventArgsPool = - new DefaultObjectPool<SocketAsyncEventArgs>(new DefaultPooledObjectPolicy<SocketAsyncEventArgs>(), - maxPooledObjects); - - for (int i = 0; i < maxPooledObjects; i++) - { - SocketAsyncEventArgs receiveFromArgs = new SocketAsyncEventArgs(); - receiveFromArgs.Completed += HandleIOCompleted; - receiveFromAsyncEventArgsPool.Return(receiveFromArgs); - } - } - - /// <summary> - /// Provides an awaitable wrapper around an asynchronous socket receive operation. - /// </summary> - /// <param name="socket">The socket which should receive data from the remote endpoint.</param> - /// <param name="remoteEndPoint">The remove endpoint from which data should be received.</param> - /// <param name="socketFlags">The socket flags associated with the receive operation.</param> - /// <param name="inputBuffer">The memory buffer into which received data will be stored.</param> - /// <param name="cancellationToken">The cancellation token to observe for the operation.</param> - /// <returns>The result of the receive operation.</returns> - public Task<TransmissionResult> ReceiveFromAsync(Socket socket, EndPoint remoteEndPoint, SocketFlags socketFlags, - Memory<byte> inputBuffer, CancellationToken cancellationToken = default) - { - TaskCompletionSource<TransmissionResult> tcs = new TaskCompletionSource<TransmissionResult>(); - - byte[] rentedReceiveFromBuffer = receiveFromBufferPool.Rent(PacketBufferLength); - Memory<byte> rentedReceiveFromBufferMemory = new Memory<byte>(rentedReceiveFromBuffer); - - SocketAsyncEventArgs args = receiveFromAsyncEventArgsPool.Get(); - args.SetBuffer(rentedReceiveFromBufferMemory); - args.SocketFlags = socketFlags; - args.RemoteEndPoint = remoteEndPoint; - args.UserToken = new AsyncReadToken(rentedReceiveFromBuffer, inputBuffer, tcs, cancellationToken); - - /* - // register cleanup action for when the cancellation token is thrown - cancellationToken.Register(() => - { - tcs.SetCanceled(); - - receiveBufferPool.Return(rentedReceiveFromBuffer, true); - - //TODO this is probably a hideous solution. find a better one - args.Completed -= HandleIOCompleted; - args.Dispose(); - - SocketAsyncEventArgs newArgs = new SocketAsyncEventArgs(); - newArgs.Completed += HandleIOCompleted; - receiveAsyncEventArgsPool.Return(newArgs); - }); - */ - - // if the receive operation doesn't complete synchronously, returns the awaitable task - if (socket.ReceiveFromAsync(args)) return tcs.Task; - - args.MemoryBuffer.CopyTo(inputBuffer); - - TransmissionResult result = new TransmissionResult(args); - - receiveFromBufferPool.Return(rentedReceiveFromBuffer, true); - receiveFromAsyncEventArgsPool.Return(args); - - return Task.FromResult(result); - } - } -} -\ No newline at end of file diff --git a/NetSharp/NetSharp/Sockets/SocketServer.cs b/NetSharp/NetSharp/Sockets/SocketServer.cs @@ -1,95 +1,35 @@ -using System; +using NetSharp.Packets; + +using System.Buffers; +using System.Collections.Concurrent; using System.Net; using System.Net.Sockets; using System.Threading; using System.Threading.Tasks; -using Microsoft.Extensions.ObjectPool; -using NetSharp.Utils; namespace NetSharp.Sockets { - public class SocketServer : IDisposable + public abstract class SocketServer : SocketConnection { - private readonly ObjectPool<SocketAsyncEventArgs> transmissionArgsPool; + protected readonly ConcurrentDictionary<EndPoint, Task> ConnectedClientHandlerTasks; - /// <summary> - /// Destroys a socket server instance. - /// </summary> - ~SocketServer() - { - Dispose(false); - } + protected readonly ArrayPool<byte> BufferPool; - /// <summary> - /// The socket which should be used to listen for incoming data and to send outgoing data. - /// </summary> - protected readonly Socket listenerSocket; - - /// <summary> - /// Implementation of dispose pattern. - /// </summary> - /// <param name="disposing"> - /// Whether this method is being called by the object finalizer, or by the <see cref="Dispose()"/> method. - /// </param> - protected virtual void Dispose(bool disposing) + protected SocketServer(in AddressFamily connectionAddressFamily, in SocketType connectionSocketType, in ProtocolType connectionProtocolType) + : base(in connectionAddressFamily, in connectionSocketType, in connectionProtocolType) { - if (disposing) - { - listenerSocket.Dispose(); - } - } + BufferPool = ArrayPool<byte>.Create(NetworkPacket.TotalSize, 10); - public SocketServer(AddressFamily listenerAddressFamily, SocketType listenerSocketType, - ProtocolType listenerProtocolType) - { - listenerSocket = new Socket(listenerAddressFamily, listenerSocketType, listenerProtocolType); - - transmissionArgsPool = new DefaultObjectPool<SocketAsyncEventArgs>(new DefaultPooledObjectPolicy<SocketAsyncEventArgs>()); + ConnectedClientHandlerTasks = new ConcurrentDictionary<EndPoint, Task>(); } - /// <inheritdoc /> - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } + protected abstract SocketAsyncEventArgs GenerateConnectionArgs(EndPoint remoteEndPoint); - public ValueTask<TransmissionResult> ReceiveAsync(EndPoint remoteEndPoint, SocketFlags receiveFlags, Memory<byte> receiveBuffer, - CancellationToken cancellationToken = default) - { - return SocketOperations.ReceiveFromAsync(transmissionArgsPool, listenerSocket, - remoteEndPoint, receiveFlags, receiveBuffer, cancellationToken); - } - - public ValueTask<TransmissionResult> SendAsync(EndPoint remoteEndPoint, SocketFlags sendFlags, Memory<byte> sendBuffer, - CancellationToken cancellationToken = default) - { - return SocketOperations.SendToAsync(transmissionArgsPool, listenerSocket, - remoteEndPoint, sendFlags, sendBuffer, cancellationToken); - } - - public Task<bool> TryBindAsync(EndPoint localEndPoint, TimeSpan timeout) - { - using CancellationTokenSource cts = new CancellationTokenSource(timeout); + protected abstract void DestroyConnectionArgs(SocketAsyncEventArgs remoteConnectionArgs); - try - { - return Task.Run(() => - { - listenerSocket.Bind(localEndPoint); + protected abstract Task HandleClient(SocketAsyncEventArgs clientArgs, + CancellationToken cancellationToken = default); - return true; - }, cts.Token); - } - catch (TaskCanceledException) - { - return Task.FromResult(false); - } - catch (SocketException ex) - { - Console.WriteLine($"Socket exception on binding socket to {localEndPoint}: {ex}"); - return Task.FromResult(false); - } - } + public abstract Task RunAsync(CancellationToken cancellationToken = default); } } \ No newline at end of file diff --git a/NetSharp/NetSharp/Sockets/SocketWriter.cs b/NetSharp/NetSharp/Sockets/SocketWriter.cs @@ -1,144 +0,0 @@ -using System; -using System.Buffers; -using System.Net; -using System.Net.Sockets; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Extensions.ObjectPool; -using NetSharp.Packets; - -namespace NetSharp.Sockets -{ - /// <summary> - /// Helper class providing awaitable wrappers around asynchronous Send and SendTo operations. - /// </summary> - public sealed class SocketWriter - { - private readonly int PacketBufferLength; - private readonly ObjectPool<SocketAsyncEventArgs> sendToAsyncEventArgsPool; - private readonly ArrayPool<byte> sendToBufferPool; - - private void HandleIOCompleted(object? sender, SocketAsyncEventArgs args) - { - switch (args.LastOperation) - { - case SocketAsyncOperation.SendTo: - AsyncWriteToken asyncSendToToken = (AsyncWriteToken)args.UserToken; - - if (asyncSendToToken.CancellationToken.IsCancellationRequested) - { - asyncSendToToken.CompletionSource.SetCanceled(); - } - else - { - if (args.SocketError != SocketError.Success) - { - asyncSendToToken.CompletionSource.SetException( - new SocketException((int)args.SocketError)); - } - else - { - asyncSendToToken.CompletionSource.SetResult(args.BytesTransferred); - } - } - - sendToBufferPool.Return(asyncSendToToken.RentedBuffer, true); - sendToAsyncEventArgsPool.Return(args); - break; - - default: - throw new InvalidOperationException( - $"The {nameof(SocketWriter)} class doesn't support the {args.LastOperation} operation."); - } - } - - private readonly struct AsyncWriteToken - { - public readonly CancellationToken CancellationToken; - public readonly TaskCompletionSource<int> CompletionSource; - public readonly byte[] RentedBuffer; - - public AsyncWriteToken(byte[] rentedBuffer, TaskCompletionSource<int> tcs, - CancellationToken cancellationToken = default) - { - RentedBuffer = rentedBuffer; - - CompletionSource = tcs; - CancellationToken = cancellationToken; - } - } - - internal SocketWriter(int packetBufferLength = NetworkPacket.PacketSize, int maxPooledObjects = 10, - bool preallocateBuffers = false) - { - PacketBufferLength = packetBufferLength; - - sendToBufferPool = ArrayPool<byte>.Create(packetBufferLength, maxPooledObjects); - - sendToAsyncEventArgsPool = - new DefaultObjectPool<SocketAsyncEventArgs>(new DefaultPooledObjectPolicy<SocketAsyncEventArgs>(), - maxPooledObjects); - - for (int i = 0; i < maxPooledObjects; i++) - { - SocketAsyncEventArgs sendToArgs = new SocketAsyncEventArgs(); - sendToArgs.Completed += HandleIOCompleted; - sendToAsyncEventArgsPool.Return(sendToArgs); - } - } - - /// <summary> - /// Provides an awaitable wrapper around an asynchronous socket send operation. - /// </summary> - /// <param name="socket">The socket which should send the data to the remote endpoint.</param> - /// <param name="remoteEndPoint">The remote endpoint to which data should be written.</param> - /// <param name="socketFlags">The socket flags associated with the send operation.</param> - /// <param name="outputBuffer">The data buffer which should be sent.</param> - /// <param name="cancellationToken">The cancellation token to observe for the operation.</param> - /// <returns>The number of bytes of data which were written to the remote endpoint.</returns> - public ValueTask<int> SendToAsync(Socket socket, EndPoint remoteEndPoint, SocketFlags socketFlags, - Memory<byte> outputBuffer, CancellationToken cancellationToken = default) - { - TaskCompletionSource<int> tcs = new TaskCompletionSource<int>(); - - byte[] rentedSendToBuffer = sendToBufferPool.Rent(PacketBufferLength); - Memory<byte> rentedSendToBufferMemory = new Memory<byte>(rentedSendToBuffer); - - outputBuffer.CopyTo(rentedSendToBufferMemory); - - SocketAsyncEventArgs args = sendToAsyncEventArgsPool.Get(); - args.SetBuffer(rentedSendToBufferMemory); - args.SocketFlags = socketFlags; - args.RemoteEndPoint = remoteEndPoint; - args.UserToken = new AsyncWriteToken(rentedSendToBuffer, tcs, cancellationToken); - - /* - // register cleanup action for when the cancellation token is thrown - cancellationToken.Register(() => - { - tcs.SetCanceled(); - - sendBufferPool.Return(rentedSendToBuffer, true); - - //TODO this is probably a hideous solution. find a better one - args.Completed -= HandleIOCompleted; - args.Dispose(); - - SocketAsyncEventArgs newArgs = new SocketAsyncEventArgs(); - newArgs.Completed += HandleIOCompleted; - sendAsyncEventArgsPool.Return(newArgs); - }); - */ - - // if the send operation doesn't complete synchronously, return the awaitable task - if (socket.SendToAsync(args)) return new ValueTask<int>(tcs.Task); - - int result = args.BytesTransferred; - - sendToBufferPool.Return(rentedSendToBuffer, true); - sendToAsyncEventArgsPool.Return(args); - - return new ValueTask<int>(result); - } - } -} -\ No newline at end of file diff --git a/NetSharp/NetSharp/Sockets/Stream/StreamSocketClient.cs b/NetSharp/NetSharp/Sockets/Stream/StreamSocketClient.cs @@ -0,0 +1,23 @@ +using System.Net; +using System.Net.Sockets; + +namespace NetSharp.Sockets.Stream +{ + public class StreamSocketClient : SocketClient + { + public StreamSocketClient(in AddressFamily connectionAddressFamily, in ProtocolType connectionProtocolType) + : base(in connectionAddressFamily, SocketType.Stream, in connectionProtocolType) + { + } + + public void Connect(in EndPoint remoteEndPoint) + { + connection.Connect(remoteEndPoint); + } + + public void Disconnect() + { + connection.Disconnect(true); + } + } +} +\ No newline at end of file diff --git a/NetSharp/NetSharp/Sockets/Stream/StreamSocketServer.cs b/NetSharp/NetSharp/Sockets/Stream/StreamSocketServer.cs @@ -0,0 +1,145 @@ +using System; +using System.Collections.Concurrent; +using System.Net; +using System.Net.Sockets; +using System.Text; +using System.Threading; +using System.Threading.Channels; +using System.Threading.Tasks; +using NetSharp.Packets; +using NetSharp.Utils; + +namespace NetSharp.Sockets.Stream +{ + public class StreamSocketServer : SocketServer + { + private readonly ConcurrentDictionary<EndPoint, RemoteStreamClientToken> connectedClientTokens; + + private readonly struct RemoteStreamClientToken + { + private readonly Channel<NetworkPacket> PacketChannel; + + public readonly ChannelReader<NetworkPacket> PacketReader; + + public readonly ChannelWriter<NetworkPacket> PacketWriter; + + public RemoteStreamClientToken(in Channel<NetworkPacket> packetChannel) + { + PacketChannel = packetChannel; + PacketReader = packetChannel.Reader; + PacketWriter = packetChannel.Writer; + } + } + + public StreamSocketServer(in AddressFamily connectionAddressFamily, in ProtocolType connectionProtocolType) + : base(in connectionAddressFamily, SocketType.Stream, in connectionProtocolType) + { + connectedClientTokens = new ConcurrentDictionary<EndPoint, RemoteStreamClientToken>(); + } + + protected override SocketAsyncEventArgs GenerateConnectionArgs(EndPoint remoteEndPoint) + { + SocketAsyncEventArgs connectionArgs = new SocketAsyncEventArgs { RemoteEndPoint = remoteEndPoint }; + + connectionArgs.Completed += SocketAsyncOperations.HandleIoCompleted; + + return connectionArgs; + } + + protected override void DestroyConnectionArgs(SocketAsyncEventArgs remoteConnectionArgs) + { + remoteConnectionArgs.AcceptSocket.Shutdown(SocketShutdown.Both); + remoteConnectionArgs.AcceptSocket.Close(); + + remoteConnectionArgs.Completed -= SocketAsyncOperations.HandleIoCompleted; + + remoteConnectionArgs.Dispose(); + } + + protected override async Task HandleClient(SocketAsyncEventArgs clientArgs, CancellationToken cancellationToken = default) + { + EndPoint clientEndPoint = clientArgs.AcceptSocket.RemoteEndPoint; + RemoteStreamClientToken clientToken = connectedClientTokens[clientEndPoint]; + + Socket clientSocket = clientArgs.AcceptSocket; + + byte[] requestBuffer = new byte[NetworkPacket.TotalSize]; + Memory<byte> requestBufferMemory = new Memory<byte>(requestBuffer); + + byte[] responseBuffer = new byte[NetworkPacket.TotalSize]; + Memory<byte> responseBufferMemory = new Memory<byte>(responseBuffer); + + try + { + while (!cancellationToken.IsCancellationRequested) + { + TransmissionResult receiveResult = + await SocketAsyncOperations + .ReceiveAsync(clientArgs, clientSocket, clientEndPoint, SocketFlags.None, requestBufferMemory, cancellationToken) + .ConfigureAwait(false); + + if (receiveResult.Count == 0) + { + break; + } +#if DEBUG + lock (typeof(Console)) + { + Console.WriteLine($"[Server] Received {receiveResult.Count} bytes from {receiveResult.RemoteEndPoint}"); + Console.WriteLine($"[Server] <<<< {Encoding.UTF8.GetString(receiveResult.Buffer.Span)}"); + } +#endif + + NetworkPacket request = NetworkPacket.Deserialise(requestBufferMemory); + + // TODO implement actual request handling, besides just an echo + NetworkPacket response = request; + + NetworkPacket.Serialise(response, responseBufferMemory); + + TransmissionResult sendResult = + await SocketAsyncOperations + .SendAsync(clientArgs, clientSocket, clientEndPoint, SocketFlags.None, responseBufferMemory, cancellationToken) + .ConfigureAwait(false); + +#if DEBUG + lock (typeof(Console)) + { + Console.WriteLine($"[Server] Sent {sendResult.Count} bytes to {sendResult.RemoteEndPoint}"); + Console.WriteLine($"[Server] >>>> {Encoding.UTF8.GetString(sendResult.Buffer.Span)}"); + } +#endif + } + } + catch (OperationCanceledException) { } + finally + { + DestroyConnectionArgs(clientArgs); + } + } + + public override async Task RunAsync(CancellationToken cancellationToken = default) + { + connection.Listen(100); + + EndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0); + + while (!cancellationToken.IsCancellationRequested) + { + SocketAsyncEventArgs clientArgs = GenerateConnectionArgs(remoteEndPoint); + + await SocketAsyncOperations.AcceptAsync(clientArgs, connection, cancellationToken); + + EndPoint clientEndPoint = clientArgs.AcceptSocket.RemoteEndPoint; + + BoundedChannelOptions clientChannelOptions = new BoundedChannelOptions(60) + { FullMode = BoundedChannelFullMode.DropOldest, SingleReader = true, SingleWriter = true }; + Channel<NetworkPacket> clientChannel = Channel.CreateBounded<NetworkPacket>(clientChannelOptions); + + connectedClientTokens[clientEndPoint] = new RemoteStreamClientToken(in clientChannel); + + ConnectedClientHandlerTasks[clientEndPoint] = HandleClient(clientArgs, cancellationToken); + } + } + } +} +\ No newline at end of file diff --git a/NetSharp/NetSharp/Utils/BiDictionary.cs b/NetSharp/NetSharp/Utils/BiDictionary.cs @@ -1,6 +1,6 @@ using System.Collections.Concurrent; -namespace NetSharp.Utils +namespace NetSharp.Deprecated { /// <summary> /// Represents a concurrent two-way dictionary, that can be indexed by either a key or a value. diff --git a/NetSharp/NetSharp/Utils/Constants.cs b/NetSharp/NetSharp/Utils/Constants.cs @@ -1,15 +0,0 @@ -namespace NetSharp.Utils -{ - /// <summary> - /// Holds internal default configurations and constants. - /// </summary> - internal static class Constants - { - /// <summary> - /// The default port over which a connection is made. - /// </summary> - internal const int DefaultPort = 12374; - - internal const int MaximumUdpPacketBytes = 65507; - } -} -\ No newline at end of file diff --git a/NetSharp/NetSharp/Utils/Conversion/EndianAwareBitConverter.cs b/NetSharp/NetSharp/Utils/Conversion/EndianAwareBitConverter.cs @@ -1,7 +1,7 @@ using System; using System.Runtime.CompilerServices; -namespace NetSharp.Utils.Conversion +namespace NetSharp.Deprecated.Conversion { /// <summary> /// Wraps the <see cref="BitConverter"/> class to provide conversion that is endian-aware. diff --git a/NetSharp/NetSharp/Utils/CryptographyHelpers.cs b/NetSharp/NetSharp/Utils/CryptographyHelpers.cs @@ -1,96 +0,0 @@ -using System; -using System.IO; -using System.Security.Cryptography; -using System.Text; - -namespace NetSharp.Utils -{ - internal static class CryptographyHelpers - { - #region Settings - - private static string _hash = "SHA1"; - private static int _iterations = 2; - private static int _keySize = 256; - private static string _salt = "aselrias38490a32"; // Random - private static string _vector = "8947az34awl34kjq"; // Random - - #endregion Settings - - public static string Decrypt(byte[] value, string password) - { - return Decrypt<AesManaged>(value, password); - } - - public static string Decrypt<T>(byte[] value, string password) where T : SymmetricAlgorithm, new() - { - byte[] vectorBytes = Encoding.ASCII.GetBytes(_vector); // GetBytes<ASCIIEncoding>(_vector); - byte[] saltBytes = Encoding.ASCII.GetBytes(_salt); // GetBytes<ASCIIEncoding>(_salt); - byte[] valueBytes = value; - - byte[] decrypted; - int decryptedByteCount = 0; - - using (T cipher = new T()) - { - PasswordDeriveBytes _passwordBytes = new PasswordDeriveBytes(password, saltBytes, _hash, _iterations); - byte[] keyBytes = _passwordBytes.GetBytes(_keySize / 8); - - cipher.Mode = CipherMode.CBC; - - try - { - using (ICryptoTransform decryptor = cipher.CreateDecryptor(keyBytes, vectorBytes)) - { - using MemoryStream from = new MemoryStream(valueBytes); - using CryptoStream reader = new CryptoStream(@from, decryptor, CryptoStreamMode.Read); - - decrypted = new byte[valueBytes.Length]; - decryptedByteCount = reader.Read(decrypted, 0, decrypted.Length); - } - } - catch (Exception ex) - { - return String.Empty; - } - - cipher.Clear(); - } - return Encoding.UTF8.GetString(decrypted, 0, decryptedByteCount); - } - - public static byte[] Encrypt(string value, string password) - { - return Encrypt<AesManaged>(value, password); - } - - public static byte[] Encrypt<T>(string value, string password) where T : SymmetricAlgorithm, new() - { - byte[] vectorBytes = Encoding.ASCII.GetBytes(_vector); // GetBytes<ASCIIEncoding>(_vector); - byte[] saltBytes = Encoding.ASCII.GetBytes(_salt); // GetBytes<ASCIIEncoding>(_salt); - byte[] valueBytes = Encoding.UTF8.GetBytes(value); // GetBytes<UTF8Encoding>(value); - - byte[] encrypted; - using (T cipher = new T()) - { - PasswordDeriveBytes _passwordBytes = - new PasswordDeriveBytes(password, saltBytes, _hash, _iterations); - byte[] keyBytes = _passwordBytes.GetBytes(_keySize / 8); - - cipher.Mode = CipherMode.CBC; - - using (ICryptoTransform encryptor = cipher.CreateEncryptor(keyBytes, vectorBytes)) - { - using MemoryStream to = new MemoryStream(); - using CryptoStream writer = new CryptoStream(to, encryptor, CryptoStreamMode.Write); - - writer.Write(valueBytes, 0, valueBytes.Length); - writer.FlushFinalBlock(); - encrypted = to.ToArray(); - } - cipher.Clear(); - } - return encrypted; - } - } -} -\ No newline at end of file diff --git a/NetSharp/NetSharp/Utils/RingBuffer.cs b/NetSharp/NetSharp/Utils/RingBuffer.cs @@ -1,50 +0,0 @@ -using System.Threading; - -namespace NetSharp.Utils -{ - public class RingBuffer<T> - { - private readonly T[] buffer; - - private int currentIndex; - - public RingBuffer(int capacity) - { - buffer = new T[capacity]; - - Capacity = capacity; - Count = 0; - } - - public int Capacity { get; } - - public int Count { get; } - - public T Pop() - { - T removedItem = buffer[currentIndex--]; - - currentIndex = currentIndex < 0 ? currentIndex + Capacity : currentIndex; - - return removedItem; - } - - public bool Push(T newItem, out T removedItem) - { - bool overwroteItem = false; - removedItem = default; - - if (buffer[currentIndex] != null) - { - removedItem = buffer[currentIndex]; - overwroteItem = true; - } - - buffer[currentIndex] = newItem; - - currentIndex = (currentIndex + 1) % Capacity; - - return overwroteItem; - } - } -} -\ No newline at end of file diff --git a/NetSharp/NetSharpExamples/Program.cs b/NetSharp/NetSharpExamples/Program.cs @@ -1,19 +1,21 @@ -using System; +#define TCP +//#undef TCP + +using NetSharp.Sockets.Datagram; +using NetSharp.Sockets.Stream; + +using System; using System.Collections.Generic; using System.Diagnostics; -using System.IO; using System.Linq; using System.Net; using System.Net.Sockets; using System.Text; using System.Threading; using System.Threading.Tasks; -using NetSharp; -using NetSharp.Extensions; -using NetSharp.Logging; -using NetSharp.Packets; -using NetSharp.Sockets; -using NetSharp.Utils; + +using NetworkPacket = NetSharp.Packets.NetworkPacket; +using SocketServer = NetSharp.Sockets.SocketServer; namespace NetSharpExamples { @@ -21,7 +23,7 @@ namespace NetSharpExamples { private const int NetworkTimeout = 1_000_000; private const int ServerPort = 12374; - private static readonly IPAddress ServerAddress = IPAddress.Parse("10.4.3.167"); // IPAddress.Parse("192.168.0.31"); + private static readonly IPAddress ServerAddress = IPAddress.Parse("192.168.0.15"); private static readonly EndPoint ServerEndPoint = new IPEndPoint(ServerAddress, ServerPort); private static async Task Main() @@ -32,117 +34,13 @@ namespace NetSharpExamples await Task.Factory.StartNew(TestSocketClient).Result; Console.ReadLine(); - - //await Task.Factory.StartNew(TestServer); - //await Task.Factory.StartNew(TestClient).Result; - } - - #region Connection Tests - - private static async Task TestClient() - { - TimeSpan socketTimeout = TimeSpan.FromSeconds(NetworkTimeout); - - const int clientCount = 10; - const long sentPacketCount = 10_000; - - ConnectionBuilder clientBuilder = new ConnectionBuilder(); - - Console.WriteLine($"Testing client connections..."); - - for (int i = 0; i < clientCount; i++) - { - await Task.Factory.StartNew(async clientId => - { - Console.WriteLine($"Starting client {clientId}"); - - using Connection client = clientBuilder.Build(); - await client.TryBindAsync(new IPEndPoint(IPAddress.Any, 0)); - await client.TryConnectAsync(ServerEndPoint); - //client.SetLoggingStream(Console.OpenStandardOutput()); - //TimeSpan timeout = TimeSpan.FromMilliseconds(100); - Stopwatch stopwatch = new Stopwatch(); - - byte[] message = Encoding.UTF8.GetBytes("Hello World!"); - Memory<byte> messageBuffer = new Memory<byte>(message); - - NetworkPacket requestPacket = new NetworkPacket(messageBuffer, message.Length, 1, NetworkErrorCode.Ok, false); - byte[] requestPacketBuffer = new byte[NetworkPacket.PacketSize]; - NetworkPacket.SerialiseToBuffer(requestPacketBuffer, requestPacket); - - byte[] response = new byte[NetworkPacket.PacketSize]; - Memory<byte> responseBuffer = new Memory<byte>(response); - - long sentPackets = 0, receivedPackets = 0; - for (int j = 0; j < sentPacketCount; j++) - { - try - { - stopwatch.Start(); - //int sentBytes = await client.SendToAsync(serverEndPoint, requestPacketBuffer, SocketFlags.None); - int sentBytes = await client.SendAsync(ServerEndPoint, requestPacketBuffer, SocketFlags.None); - stopwatch.Stop(); - Interlocked.Increment(ref sentPackets); - - //Console.WriteLine($"[Client {clientId}] Sent {sentBytes} bytes to {serverEndPoint}"); - - stopwatch.Start(); - //TransmissionResult result = await client.ReceiveFromAsync(serverEndPoint, responseBuffer, SocketFlags.None); - TransmissionResult result = await client.ReceiveAsync(ServerEndPoint, responseBuffer, SocketFlags.None); - stopwatch.Stop(); - Interlocked.Increment(ref receivedPackets); - - //Console.WriteLine($"[Client {clientId}] Received {result.Count} bytes from {result.RemoteEndPoint}"); - } - catch (Exception ex) - { - Console.WriteLine($"[Client {clientId}] Exception: {ex}"); - } - } - - long millis = stopwatch.ElapsedMilliseconds; - double megabytes = sentPackets * NetworkPacket.DataSegmentSize / 1_000_000.0; - - Console.WriteLine($"[Client {clientId}] Sent {sentPacketCount} packets to {ServerEndPoint} in {millis} milliseconds"); - Console.WriteLine($"[Client {clientId}] Approximate bandwidth: {megabytes / (millis / 1000.0):F3} MBps"); - - await client.TryDisconnectAsync(); - Console.WriteLine($"[Client {clientId}] Closed client."); - }, i, TaskCreationOptions.LongRunning); - } - - Console.ReadLine(); } - private static async Task TestServer() - { - const string serverLogFile = @"./serverLog.txt"; - File.Delete(serverLogFile); - await using Stream serverOutputStream = File.OpenWrite(serverLogFile); - - ConnectionBuilder serverBuilder = new ConnectionBuilder(); - - using Connection server = serverBuilder.WithLogging(Console.OpenStandardOutput(), LogLevel.Info).Build(); - await server.TryBindAsync(ServerEndPoint); - //server.SetLoggingStream(Console.OpenStandardOutput()); - //server.ChangeLoggingStream(serverOutputStream, LogLevel.Error); - - Console.WriteLine("Starting server..."); - - await server.RunServerAsync(); - - Console.WriteLine("Server stopped"); - - Console.ReadLine(); - } - - #endregion Connection Tests - #region Socket Tests private static async Task TestSocketClient() { - const int clientCount = 100; + const int clientCount = 16; const long packetsToSend = 100_000; Thread[] clientThreads = new Thread[clientCount]; @@ -158,9 +56,17 @@ namespace NetSharpExamples Console.WriteLine($"[Client {id}] Starting client..."); } - using SocketClient client = new SocketClient(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); +#if TCP + using StreamSocketClient client = new StreamSocketClient(AddressFamily.InterNetwork, ProtocolType.Tcp); +#else + using DatagramSocketClient client = new DatagramSocketClient(AddressFamily.InterNetwork, ProtocolType.Udp); +#endif - if (!await client.TryBindAsync(new IPEndPoint(IPAddress.Any, 0), TimeSpan.FromMilliseconds(1_000))) + try + { + client.Bind(new IPEndPoint(IPAddress.Any, 0)); + } + catch (SocketException) { lock (typeof(Console)) { @@ -168,45 +74,63 @@ namespace NetSharpExamples return; } } +#if TCP + client.Connect(in ServerEndPoint); +#endif - byte[] requestBuffer = new byte[NetworkPacket.PacketSize]; + byte[] requestBuffer = new byte[NetworkPacket.TotalSize]; Memory<byte> requestBufferMemory = new Memory<byte>(requestBuffer); - byte[] responseBuffer = new byte[NetworkPacket.PacketSize]; + byte[] responseBuffer = new byte[NetworkPacket.TotalSize]; Memory<byte> responseBufferMemory = new Memory<byte>(responseBuffer); Stopwatch rttStopwatch = new Stopwatch(); Stopwatch bandwidthStopwatch = new Stopwatch(); + long minRttTicks = int.MaxValue, maxRttTicks = int.MinValue; + long minRttMs = int.MaxValue, maxRttMs = int.MinValue; + for (int i = 0; i < packetsToSend; i++) { Encoding.UTF8.GetBytes($"Hello World! (Packet {i})").CopyTo(requestBufferMemory); rttStopwatch.Start(); bandwidthStopwatch.Start(); - TransmissionResult sendResult = await client.SendAsync(ServerEndPoint, SocketFlags.None, requestBufferMemory); +#if TCP + int sendResult = client.SendBytes(requestBufferMemory); +#else + int sendResult = client.SendBytesTo(requestBufferMemory, ServerEndPoint); +#endif + bandwidthStopwatch.Stop(); rttStopwatch.Stop(); #if DEBUG lock (typeof(Console)) { - //Console.WriteLine($"[Client {id}, Packet {i}] Sent {sendResult.Count} bytes to {sendResult.RemoteEndPoint}"); - //Console.WriteLine($"[Client {id}, Packet {i}] >>>> {Encoding.UTF8.GetString(sendResult.Buffer.Span)}"); + Console.WriteLine($"[Client {id}, Packet {i}] Sent {sendResult} bytes to {ServerEndPoint}"); + Console.WriteLine($"[Client {id}, Packet {i}] >>>> {Encoding.UTF8.GetString(requestBufferMemory.Span)}"); } #endif rttStopwatch.Start(); bandwidthStopwatch.Start(); - TransmissionResult receiveResult = await client.ReceiveAsync(ServerEndPoint, SocketFlags.None, responseBufferMemory); + EndPoint serverEndPoint = ServerEndPoint; + +#if TCP + int receiveResult = client.ReceiveBytes(responseBufferMemory); +#else + int receiveResult = client.ReceiveBytesFrom(responseBufferMemory, ref serverEndPoint); +#endif + bandwidthStopwatch.Stop(); rttStopwatch.Stop(); #if DEBUG lock (typeof(Console)) { - //Console.WriteLine($"[Client {id}, Packet {i}] Received {receiveResult.Count} bytes from {receiveResult.RemoteEndPoint}"); - //Console.WriteLine($"[Client {id}, Packet {i}] <<<< {Encoding.UTF8.GetString(receiveResult.Buffer.Span)}"); + Console.WriteLine($"[Client {id}, Packet {i}] Received {receiveResult} bytes from {serverEndPoint}"); + Console.WriteLine($"[Client {id}, Packet {i}] <<<< {Encoding.UTF8.GetString(responseBufferMemory.Span)}"); } #endif @@ -217,13 +141,36 @@ namespace NetSharpExamples activeThreads.Add((int)id); } - //Console.WriteLine($"[Client {id}] Client Round Trip Time: {rttStopwatch.ElapsedMilliseconds} ms"); +#if DEBUG + Console.WriteLine($"[Client {id}] Client Round Trip Time: {rttStopwatch.ElapsedTicks} ticks ({rttStopwatch.ElapsedMilliseconds} ms)"); +#endif + minRttTicks = rttStopwatch.ElapsedTicks < minRttTicks + ? rttStopwatch.ElapsedTicks + : minRttTicks; + + minRttMs = rttStopwatch.ElapsedMilliseconds < minRttMs + ? rttStopwatch.ElapsedMilliseconds + : minRttMs; + + maxRttTicks = rttStopwatch.ElapsedTicks > maxRttTicks + ? rttStopwatch.ElapsedTicks + : maxRttTicks; + + maxRttMs = rttStopwatch.ElapsedMilliseconds > maxRttMs + ? rttStopwatch.ElapsedMilliseconds + : maxRttMs; + rttStopwatch.Reset(); } } +#if TCP + client.Disconnect(); + client.Shutdown(SocketShutdown.Both); +#endif + long millis = bandwidthStopwatch.ElapsedMilliseconds; - double megabytes = packetsToSend * NetworkPacket.DataSegmentSize / 1_000_000.0; + double megabytes = packetsToSend * NetworkPacket.DataSize / 1_000_000.0; double bandwidth = megabytes / (millis / 1000.0); clientBandwidths[(int)id] = bandwidth; @@ -232,6 +179,9 @@ namespace NetSharpExamples { Console.WriteLine($"[Client {id}] Sent {packetsToSend} packets to {ServerEndPoint} in {millis} milliseconds"); Console.WriteLine($"[Client {id}] Approximate bandwidth: {bandwidth:F3} MBps"); + + Console.WriteLine($"[Client {id}] Min RTT: {minRttTicks} ticks, {minRttMs} ms"); + Console.WriteLine($"[Client {id}] Max RTT: {maxRttTicks} ticks, {maxRttMs} ms"); Console.WriteLine($"[Client {id}] Stopping client..."); } @@ -270,25 +220,17 @@ namespace NetSharpExamples private static async Task TestSocketServer() { - using SocketServer server = - new SocketServer(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); - await server.TryBindAsync(ServerEndPoint, TimeSpan.FromMilliseconds(1_000)); - - byte[] requestBuffer = new byte[NetworkPacket.PacketSize]; - Memory<byte> requestBufferMemory = new Memory<byte>(requestBuffer); - - while (true) - { - EndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0); +#if TCP + using SocketServer server = new StreamSocketServer(AddressFamily.InterNetwork, ProtocolType.Tcp); +#else + using SocketServer server = new DatagramSocketServer(AddressFamily.InterNetwork, ProtocolType.Udp); +#endif - TransmissionResult receiveResult = - await server.ReceiveAsync(remoteEndPoint, SocketFlags.None, requestBufferMemory); + server.Bind(in ServerEndPoint); - TransmissionResult sendResult = - await server.SendAsync(receiveResult.RemoteEndPoint, SocketFlags.None, requestBuffer); - } + await server.RunAsync(); } - #endregion Socket Tests +#endregion Socket Tests } } \ No newline at end of file