NetSharp

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

commit efce29d97388bd19c651aab3996475308ccfa0ee
parent cbc87b28ece603083065b5cb2b481f5d0d0f4eba
Author: Mikolaj Lenczewski <33129490+EnderRifter@users.noreply.github.com>
Date:   Fri, 21 Feb 2020 19:05:38 +0000

Now we have a performance problem. Good job me...

Diffstat:
MNetSharp/NetSharp/Client.cs | 97+++++++++++++++++++++++++++++++++++++++----------------------------------------
MNetSharp/NetSharp/Clients/TcpClient.cs | 1+
MNetSharp/NetSharp/Clients/UdpClient.cs | 1+
MNetSharp/NetSharp/Connection.cs | 165++++++++++++++++++++++++++++++++++++++-----------------------------------------
MNetSharp/NetSharp/Logging/Logger.cs | 25++++++++++---------------
MNetSharp/NetSharp/NetSharp.xml | 396+++++++++++++++++++++++++++++++++++++++----------------------------------------
MNetSharp/NetSharp/Packets/Packet.cs | 2+-
MNetSharp/NetSharp/Server.cs | 305+++++++++++++++++++++++++++++++++++++++++++------------------------------------
MNetSharp/NetSharp/Servers/TcpServer.cs | 56++++++++++++++++++++++++++++++--------------------------
MNetSharp/NetSharp/Servers/UdpServer.cs | 86+++++++++++++++++++++++++++++++++++++++++++------------------------------------
MNetSharp/NetSharp/Utils/Constants.cs | 5-----
MNetSharp/NetSharp/Utils/Conversion/EndianAwareBitConverter.cs | 20++++++++++++++++++++
MNetSharp/NetSharp/Utils/NetworkOperations.cs | 212++++++++++++++++++++++++++++++++-----------------------------------------------
MNetSharp/NetSharpExamples/Program.cs | 7+++----
14 files changed, 688 insertions(+), 690 deletions(-)

diff --git a/NetSharp/NetSharp/Client.cs b/NetSharp/NetSharp/Client.cs @@ -1,6 +1,7 @@ using System; using System.Net; using System.Net.Sockets; +using System.Reflection; using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; @@ -17,9 +18,23 @@ namespace NetSharp public abstract class Client : Connection, IClient, IDisposable { /// <summary> - /// Provides <see cref="CancellationToken"/> instances for cancelling methods after a timeout period. + /// Initialises a new instance of the <see cref="Client"/> class. + /// </summary> + private Client() + { + remoteEndPoint = new IPEndPoint(IPAddress.None, IPEndPoint.MinPort); + socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + + socketOptions = new DefaultSocketOptions(ref socket); + } + + /// <summary> + /// Destroys an instance of the <see cref="Client"/> class. /// </summary> - protected readonly CancellationTokenSource cancellationTokenSource; + ~Client() + { + Dispose(false); + } /// <summary> /// The <see cref="Socket"/> underlying the connection. @@ -39,19 +54,6 @@ namespace NetSharp /// <summary> /// Initialises a new instance of the <see cref="Client"/> class. /// </summary> - private Client() - { - cancellationTokenSource = new CancellationTokenSource(); - - remoteEndPoint = new IPEndPoint(IPAddress.None, IPEndPoint.MinPort); - socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); - - socketOptions = new DefaultSocketOptions(ref socket); - } - - /// <summary> - /// Initialises a new instance of the <see cref="Client"/> 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> @@ -68,28 +70,6 @@ namespace NetSharp } /// <summary> - /// Destroys an instance of the <see cref="Client"/> class. - /// </summary> - ~Client() - { - Dispose(false); - } - - /// <inheritdoc /> - public event Action<EndPoint>? Connected; - - /// <inheritdoc /> - public event Action<EndPoint>? Disconnected; - - /// <summary> - /// The configured socket options for the underlying connection. - /// </summary> - public SocketOptions SocketOptions - { - get { return socketOptions; } - } - - /// <summary> /// Disposes of this <see cref="Client"/> instance. /// </summary> /// <param name="disposing">Whether this instance is being disposed.</param> @@ -97,8 +77,7 @@ namespace NetSharp { if (disposing) { - cancellationTokenSource?.Dispose(); - socket?.Dispose(); + socket.Dispose(); } base.Dispose(disposing); @@ -118,6 +97,20 @@ namespace NetSharp [MethodImpl(MethodImplOptions.AggressiveInlining)] protected void OnDisconnected(EndPoint endPoint) => Disconnected?.Invoke(endPoint); + /// <inheritdoc /> + public event Action<EndPoint>? Connected; + + /// <inheritdoc /> + public event Action<EndPoint>? Disconnected; + + /// <summary> + /// The configured socket options for the underlying connection. + /// </summary> + public SocketOptions SocketOptions + { + get { return socketOptions; } + } + /// <summary> /// Disconnects the client from the remote endpoint. /// </summary> @@ -149,41 +142,43 @@ namespace NetSharp public abstract Task<bool> SendSimpleAsync<Req>(Req request, TimeSpan timeout) where Req : IRequestPacket, new(); /// <inheritdoc /> - public async Task<bool> TryBindAsync(IPAddress? localAddress, int? localPort, TimeSpan timeout) + public Task<bool> TryBindAsync(IPAddress? localAddress, int? localPort, TimeSpan timeout) { + CancellationTokenSource cts = new CancellationTokenSource(timeout); EndPoint localEndPoint = new IPEndPoint(localAddress ?? IPAddress.Any, localPort ?? 0); try { - cancellationTokenSource.CancelAfter(timeout); - - return await Task.Run(() => + return Task.Run(() => { socket.Bind(localEndPoint); return true; - }, cancellationTokenSource.Token); + }, cts.Token); } catch (TaskCanceledException) { - return false; + return Task.FromResult(false); } catch (SocketException ex) { logger.LogException($"Socket exception on binding socket to {localEndPoint}:", ex); - return false; + return Task.FromResult(false); + } + finally + { + cts.Dispose(); } } /// <inheritdoc /> public async Task<bool> TryConnectAsync(IPAddress remoteAddress, int remotePort, TimeSpan timeout) { + CancellationTokenSource cts = new CancellationTokenSource(timeout); remoteEndPoint = new IPEndPoint(remoteAddress, remotePort); try { - cancellationTokenSource.CancelAfter(timeout); - return await Task.Run(async () => { await socket.ConnectAsync(remoteEndPoint); @@ -194,7 +189,7 @@ namespace NetSharp OnConnected(SocketOptions.RemoteIPEndPoint); return true; - }, cancellationTokenSource.Token); + }, cts.Token); } catch (TaskCanceledException) { @@ -205,6 +200,10 @@ namespace NetSharp logger.LogException($"Socket exception on connection to {remoteEndPoint}:", ex); return false; } + finally + { + cts.Dispose(); + } } } } \ No newline at end of file diff --git a/NetSharp/NetSharp/Clients/TcpClient.cs b/NetSharp/NetSharp/Clients/TcpClient.cs @@ -1,5 +1,6 @@ using System; using System.Net.Sockets; +using System.Threading; using System.Threading.Tasks; using NetSharp.Packets; using NetSharp.Packets.Builtin; diff --git a/NetSharp/NetSharp/Clients/UdpClient.cs b/NetSharp/NetSharp/Clients/UdpClient.cs @@ -1,5 +1,6 @@ using System; using System.Net.Sockets; +using System.Threading; using System.Threading.Tasks; using NetSharp.Packets; using NetSharp.Packets.Builtin; diff --git a/NetSharp/NetSharp/Connection.cs b/NetSharp/NetSharp/Connection.cs @@ -41,16 +41,6 @@ namespace NetSharp } /// <summary> - /// Signifies that some data has been received from the remote endpoint. - /// </summary> - public event Action<EndPoint, int>? BytesReceived; - - /// <summary> - /// Signifies that some data was sent to the remote endpoint. - /// </summary> - public event Action<EndPoint, int>? BytesSent; - - /// <summary> /// Disposes of this <see cref="Connection"/> instance. /// </summary> /// <param name="disposing">Whether this instance is being disposed.</param> @@ -63,194 +53,189 @@ namespace NetSharp } /// <summary> - /// Listens for a packet to be received asynchronously within the given timeout, and returns the received packet. + /// Listens for a packet to be received from the network. /// </summary> /// <param name="remoteSocket">The remote socket from which to receive data.</param> /// <param name="socketFlags">The socket flags associated with the read operation.</param> /// <param name="timeout"> - /// The timespan within which to wait for a packet, returning a null packet if this limit is exceeded. + /// The timespan within which the packet should be received. After this timespan elapses, the receive task is cancelled. /// </param> + /// <param name="cancellationToken">A pre-existing cancellation token that should be observed alongside the timeout.</param> /// <returns>The packet that was received. <see cref="NullPacket"/> if not received correctly.</returns> - protected async Task<Packet> DoReceivePacketAsync(Socket remoteSocket, SocketFlags socketFlags, TimeSpan timeout) + protected Task<Packet> DoReceivePacketAsync(Socket remoteSocket, SocketFlags socketFlags, TimeSpan timeout, + CancellationToken cancellationToken = default) { - CancellationTokenSource cts = new CancellationTokenSource(timeout); + CancellationTokenSource timeoutCancellationTokenSource = new CancellationTokenSource(timeout); + CancellationTokenSource cts = + CancellationTokenSource.CreateLinkedTokenSource(timeoutCancellationTokenSource.Token, cancellationToken); try { - Packet request = - await NetworkOperations.ReadPacketAsync(remoteSocket, socketFlags, cts.Token); + return Task.Factory.StartNew(() => + { + Packet request = NetworkOperations.ReadPacket(remoteSocket, socketFlags); - OnBytesReceived(remoteSocket.RemoteEndPoint, request.TotalSize); + OnBytesReceived(remoteSocket.RemoteEndPoint, request.TotalSize); - return request; - } - catch (OperationCanceledException ex) - { - logger.LogException( - $"Could not receive a packet from {remoteSocket.RemoteEndPoint} within the given timeout ({timeout}):", - ex); - return NullPacket; + return request; + }, cts.Token); } catch (SocketException ex) { logger.LogException($"Socket exception while reading bytes from {remoteSocket.RemoteEndPoint}:", ex); - return NullPacket; + return Task.FromResult(NullPacket); } catch (Exception ex) { logger.LogException($"Exception while reading bytes from {remoteSocket.RemoteEndPoint}:", ex); - return NullPacket; + return Task.FromResult(NullPacket); } finally { cts.Dispose(); + timeoutCancellationTokenSource.Dispose(); } } /// <summary> - /// Listens for a packet to be received asynchronously within the given timeout, and returns the received packet. + /// Listens for a packet to be received from the network. /// </summary> /// <param name="socket">The socket which will receive the packet.</param> /// <param name="remoteEndPoint">The remote endpoint from which to receive the packet.</param> /// <param name="socketFlags">The socket flags associated with the read operation.</param> /// <param name="timeout"> - /// The timespan within which to wait for a packet, returning a null packet if this limit is exceeded. + /// The timespan within which the packet should be received. After this timespan elapses, the receive task is cancelled. /// </param> + /// <param name="cancellationToken">A pre-existing cancellation token that should be observed alongside the timeout.</param> /// <returns> /// The packet that was received and the associated transmission result. <see cref="NullPacket"/> if not received correctly. /// </returns> - protected async Task<(Packet request, TransmissionResult packetResult)> DoReceivePacketFromAsync( - Socket socket, EndPoint remoteEndPoint, SocketFlags socketFlags, TimeSpan timeout) + protected Task<(Packet request, TransmissionResult packetResult)> DoReceivePacketFromAsync(Socket socket, + EndPoint remoteEndPoint, SocketFlags socketFlags, TimeSpan timeout, CancellationToken cancellationToken = default) { - CancellationTokenSource cts = new CancellationTokenSource(timeout); + CancellationTokenSource timeoutCancellationTokenSource = new CancellationTokenSource(timeout); + CancellationTokenSource cts = + CancellationTokenSource.CreateLinkedTokenSource(timeoutCancellationTokenSource.Token, cancellationToken); try { - (Packet request, TransmissionResult packetResult) result = - await NetworkOperations.ReadPacketFromAsync(socket, remoteEndPoint, socketFlags, cts.Token); + return Task.Factory.StartNew(() => + { + (Packet request, TransmissionResult packetResult) result = + NetworkOperations.ReadPacketFrom(socket, remoteEndPoint, socketFlags); - OnBytesReceived(remoteEndPoint, result.request.TotalSize); + OnBytesReceived(result.packetResult.RemoteEndPoint, result.request.TotalSize); - return result; - } - catch (OperationCanceledException ex) - { - logger.LogException( - $"Could not receive a packet from {remoteEndPoint} within the given timeout ({timeout}):", ex); - return (NullPacket, NullTransmissionResult); + return result; + }, cts.Token); } catch (SocketException ex) { logger.LogException($"Socket exception while reading bytes from {remoteEndPoint}:", ex); - return (NullPacket, NullTransmissionResult); + return Task.FromResult((NullPacket, NullTransmissionResult)); } catch (Exception ex) { logger.LogException($"Exception while reading bytes from {remoteEndPoint}:", ex); - return (NullPacket, NullTransmissionResult); + return Task.FromResult((NullPacket, NullTransmissionResult)); } finally { cts.Dispose(); + timeoutCancellationTokenSource.Dispose(); } } /// <summary> - /// Sends the given packet asynchronously within the given timeout. + /// Sends the given packet to the network. /// </summary> /// <param name="remoteSocket">The remote socket to which to send the packet.</param> /// <param name="packet">The packet to send.</param> /// <param name="socketFlags">The socket flags associated with the write operation.</param> /// <param name="timeout"> - /// The timespan within which to send the packet, returning <c>false</c> if this limit is exceeded. + /// The timespan within which the packet should be received. After this timespan elapses, the send task is cancelled. /// </param> + /// <param name="cancellationToken">A pre-existing cancellation token that should be observed alongside the timeout.</param> /// <returns>Whether the packet was successfully sent.</returns> - protected async Task<bool> DoSendPacketAsync(Socket remoteSocket, Packet packet, SocketFlags socketFlags, TimeSpan timeout) + protected Task<bool> DoSendPacketAsync(Socket remoteSocket, Packet packet, SocketFlags socketFlags, TimeSpan timeout, + CancellationToken cancellationToken = default) { - CancellationTokenSource cts = new CancellationTokenSource(timeout); + CancellationTokenSource timeoutCancellationTokenSource = new CancellationTokenSource(timeout); + CancellationTokenSource cts = + CancellationTokenSource.CreateLinkedTokenSource(timeoutCancellationTokenSource.Token, cancellationToken); try { - await NetworkOperations.WritePacketAsync(remoteSocket, packet, socketFlags, cts.Token); + return Task.Factory.StartNew(() => + { + NetworkOperations.WritePacket(remoteSocket, packet, socketFlags); - OnBytesSent(remoteSocket.RemoteEndPoint, packet.TotalSize); + OnBytesSent(remoteSocket.RemoteEndPoint, packet.TotalSize); - return true; - } - catch (OperationCanceledException ex) - { - logger.LogException( - $"Could not send the packet to {remoteSocket.RemoteEndPoint} within the given timeout ({timeout}):", - ex); - return false; + return true; + }, cts.Token); } catch (SocketException ex) { logger.LogException($"Socket exception while sending bytes to {remoteSocket.RemoteEndPoint}:", ex); - return false; + return Task.FromResult(false); } catch (Exception ex) { logger.LogException($"Exception while sending bytes to {remoteSocket.RemoteEndPoint}:", ex); - return false; + return Task.FromResult(false); } finally { cts.Dispose(); + timeoutCancellationTokenSource.Dispose(); } } /// <summary> - /// Sends the given packet asynchronously within the given timeout. + /// Sends the given packet to the network. /// </summary> /// <param name="socket">The socket which should send the packet.</param> /// <param name="remoteEndPoint">The remote endpoint to which to send the packet.</param> /// <param name="packet">The packet to send.</param> /// <param name="socketFlags">The socket flags associated with the write operation.</param> /// <param name="timeout"> - /// The timespan within which to send the packet, returning <c>false</c> if this limit is exceeded. + /// The timespan within which the packet should be received. After this timespan elapses, the send task is cancelled. /// </param> + /// <param name="cancellationToken">A pre-existing cancellation token that should be observed alongside the timeout.</param> /// <returns>Whether the packet was successfully sent.</returns> - protected async Task<bool> DoSendPacketToAsync(Socket socket, EndPoint remoteEndPoint, Packet packet, - SocketFlags socketFlags, TimeSpan timeout) + protected Task<bool> DoSendPacketToAsync(Socket socket, EndPoint remoteEndPoint, Packet packet, SocketFlags socketFlags, + TimeSpan timeout, CancellationToken cancellationToken = default) { - CancellationTokenSource cts = new CancellationTokenSource(timeout); + CancellationTokenSource timeoutCancellationTokenSource = new CancellationTokenSource(timeout); + CancellationTokenSource cts = + CancellationTokenSource.CreateLinkedTokenSource(timeoutCancellationTokenSource.Token, cancellationToken); try { - if (packet.TotalSize > Constants.UdpMaxBufferSize) + return Task.Factory.StartNew(() => { - throw new Exception( - $"The given UDP packet exceeded the maximum allowed size of {Constants.UdpMaxBufferSize}, " + - "and could not be sent. Consider splitting up the packet into multiple, smaller packets " + - "that are less likely to get lost due to fragmentation, or using TCP instead."); - } + NetworkOperations.WritePacketTo(socket, remoteEndPoint, packet, socketFlags); - await NetworkOperations.WritePacketToAsync(socket, remoteEndPoint, packet, socketFlags, cts.Token); + OnBytesSent(remoteEndPoint, packet.TotalSize); - OnBytesSent(remoteEndPoint, packet.TotalSize); - - return true; - } - catch (OperationCanceledException ex) - { - logger.LogException( - $"Could not send the packet to {remoteEndPoint} within the given timeout ({timeout}):", ex); - return false; + return true; + }, cts.Token); } catch (SocketException ex) { logger.LogException($"Socket exception while sending bytes to {remoteEndPoint}:", ex); - return false; + return Task.FromResult(false); } catch (Exception ex) { logger.LogException($"Exception while sending bytes to {remoteEndPoint}:", ex); - return false; + return Task.FromResult(false); } finally { cts.Dispose(); + timeoutCancellationTokenSource.Dispose(); } } @@ -273,6 +258,16 @@ namespace NetSharp BytesSent?.Invoke(remoteEndPoint, bytesSent); /// <summary> + /// Signifies that some data has been received from the remote endpoint. + /// </summary> + public event Action<EndPoint, int>? BytesReceived; + + /// <summary> + /// Signifies that some data was sent to the remote endpoint. + /// </summary> + public event Action<EndPoint, int>? BytesSent; + + /// <summary> /// Makes the client log to the given stream. /// </summary> /// <param name="loggingStream">The stream that new messages should be logged to.</param> @@ -281,9 +276,7 @@ namespace NetSharp /// </param> public void ChangeLoggingStream(Stream loggingStream, LogLevel minimumMessageSeverityLevel = LogLevel.Info) { - logger = new Logger(loggingStream); - - logger.SetMinimumLogSeverity(minimumMessageSeverityLevel); + logger = new Logger(loggingStream, minimumMessageSeverityLevel); } /// <inheritdoc /> diff --git a/NetSharp/NetSharp/Logging/Logger.cs b/NetSharp/NetSharp/Logging/Logger.cs @@ -34,7 +34,7 @@ namespace NetSharp.Logging /// <summary> /// A simple logger capable of writing text to a stream. /// </summary> - public struct Logger : IDisposable + public readonly struct Logger : IDisposable { /// <summary> /// The stream to which messages will be logged. @@ -42,25 +42,26 @@ namespace NetSharp.Logging private readonly Stream loggingStream; /// <summary> - /// The text writer we will use to log messages to the underlying stream. + /// The minimum severity that log messages need to be logged to the underlying stream. /// </summary> - private readonly StreamWriter writer; + private readonly LogLevel minimumSeverity; /// <summary> - /// The minimum severity that log messages need to be logged to the underlying stream. + /// The text writer we will use to log messages to the underlying stream. /// </summary> - private LogLevel minimumSeverity; + private readonly StreamWriter writer; /// <summary> /// Initialises a new instance of the <see cref="Logger"/> struct. /// </summary> - /// <param name="streamToLogTo">The stream that the logger instance should log messages to.</param> - public Logger(Stream streamToLogTo) + /// <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 = streamToLogTo; + loggingStream = outputStream; writer = new StreamWriter(loggingStream, Encoding.Default) { AutoFlush = true }; - minimumSeverity = LogLevel.Info; + minimumSeverity = minimumLogSeverity; } /// <inheritdoc /> @@ -189,11 +190,5 @@ namespace NetSharp.Logging /// </summary> /// <param name="message">The warning that should be logged.</param> public async Task LogWarningAsync(string message) => await LogAsync(message, null, LogLevel.Warn); - - /// <summary> - /// Sets the minimum severity level that new messages need to be logged to the underlying stream. - /// </summary> - /// <param name="minimumSeverityLevel">The new minimum severity level.</param> - public void SetMinimumLogSeverity(LogLevel minimumSeverityLevel) => minimumSeverity = minimumSeverityLevel; } } \ No newline at end of file diff --git a/NetSharp/NetSharp/NetSharp.xml b/NetSharp/NetSharp/NetSharp.xml @@ -9,9 +9,14 @@ Provides methods for connecting to and talking with a <see cref="T:NetSharp.Interfaces.IServer"/> instance. </summary> </member> - <member name="F:NetSharp.Client.cancellationTokenSource"> + <member name="M:NetSharp.Client.#ctor"> <summary> - Provides <see cref="T:System.Threading.CancellationToken"/> instances for cancelling methods after a timeout period. + Initialises a new instance of the <see cref="T:NetSharp.Client"/> class. + </summary> + </member> + <member name="M:NetSharp.Client.Finalize"> + <summary> + Destroys an instance of the <see cref="T:NetSharp.Client"/> class. </summary> </member> <member name="F:NetSharp.Client.socket"> @@ -29,11 +34,6 @@ The remote endpoint with which this client communicates. </summary> </member> - <member name="M:NetSharp.Client.#ctor"> - <summary> - Initialises a new instance of the <see cref="T:NetSharp.Client"/> class. - </summary> - </member> <member name="M:NetSharp.Client.#ctor(System.Net.Sockets.SocketType,System.Net.Sockets.ProtocolType,NetSharp.Utils.Socket_Options.SocketOptionManager)"> <summary> Initialises a new instance of the <see cref="T:NetSharp.Client"/> class. @@ -42,22 +42,6 @@ <param name="protocolType">The protocol type for the underlying socket.</param> <param name="socketManager">The <see cref="T:NetSharp.Utils.Socket_Options.SocketOptions"/> manager to use.</param> </member> - <member name="M:NetSharp.Client.Finalize"> - <summary> - Destroys an instance of the <see cref="T:NetSharp.Client"/> class. - </summary> - </member> - <member name="E:NetSharp.Client.Connected"> - <inheritdoc /> - </member> - <member name="E:NetSharp.Client.Disconnected"> - <inheritdoc /> - </member> - <member name="P:NetSharp.Client.SocketOptions"> - <summary> - The configured socket options for the underlying connection. - </summary> - </member> <member name="M:NetSharp.Client.Dispose(System.Boolean)"> <summary> Disposes of this <see cref="T:NetSharp.Client"/> instance. @@ -76,6 +60,17 @@ </summary> <param name="endPoint">The remote endpoint with which a connection was lost.</param> </member> + <member name="E:NetSharp.Client.Connected"> + <inheritdoc /> + </member> + <member name="E:NetSharp.Client.Disconnected"> + <inheritdoc /> + </member> + <member name="P:NetSharp.Client.SocketOptions"> + <summary> + The configured socket options for the underlying connection. + </summary> + </member> <member name="M:NetSharp.Client.Disconnect"> <summary> Disconnects the client from the remote endpoint. @@ -164,70 +159,64 @@ Initialises a new instance of the <see cref="T:NetSharp.Connection"/> class. </summary> </member> - <member name="E:NetSharp.Connection.BytesReceived"> - <summary> - Signifies that some data has been received from the remote endpoint. - </summary> - </member> - <member name="E:NetSharp.Connection.BytesSent"> - <summary> - Signifies that some data was sent to the remote endpoint. - </summary> - </member> <member name="M:NetSharp.Connection.Dispose(System.Boolean)"> <summary> Disposes of this <see cref="T:NetSharp.Connection"/> instance. </summary> <param name="disposing">Whether this instance is being disposed.</param> </member> - <member name="M:NetSharp.Connection.DoReceivePacketAsync(System.Net.Sockets.Socket,System.Net.Sockets.SocketFlags,System.TimeSpan)"> + <member name="M:NetSharp.Connection.DoReceivePacketAsync(System.Net.Sockets.Socket,System.Net.Sockets.SocketFlags,System.TimeSpan,System.Threading.CancellationToken)"> <summary> - Listens for a packet to be received asynchronously within the given timeout, and returns the received packet. + Listens for a packet to be received from the network. </summary> <param name="remoteSocket">The remote socket from which to receive data.</param> <param name="socketFlags">The socket flags associated with the read operation.</param> <param name="timeout"> - The timespan within which to wait for a packet, returning a null packet if this limit is exceeded. + The timespan within which the packet should be received. After this timespan elapses, the receive task is cancelled. </param> + <param name="cancellationToken">A pre-existing cancellation token that should be observed alongside the timeout.</param> <returns>The packet that was received. <see cref="F:NetSharp.Connection.NullPacket"/> if not received correctly.</returns> </member> - <member name="M:NetSharp.Connection.DoReceivePacketFromAsync(System.Net.Sockets.Socket,System.Net.EndPoint,System.Net.Sockets.SocketFlags,System.TimeSpan)"> + <member name="M:NetSharp.Connection.DoReceivePacketFromAsync(System.Net.Sockets.Socket,System.Net.EndPoint,System.Net.Sockets.SocketFlags,System.TimeSpan,System.Threading.CancellationToken)"> <summary> - Listens for a packet to be received asynchronously within the given timeout, and returns the received packet. + Listens for a packet to be received from the network. </summary> <param name="socket">The socket which will receive the packet.</param> <param name="remoteEndPoint">The remote endpoint from which to receive the packet.</param> <param name="socketFlags">The socket flags associated with the read operation.</param> <param name="timeout"> - The timespan within which to wait for a packet, returning a null packet if this limit is exceeded. + The timespan within which the packet should be received. After this timespan elapses, the receive task is cancelled. </param> + <param name="cancellationToken">A pre-existing cancellation token that should be observed alongside the timeout.</param> <returns> The packet that was received and the associated transmission result. <see cref="F:NetSharp.Connection.NullPacket"/> if not received correctly. </returns> </member> - <member name="M:NetSharp.Connection.DoSendPacketAsync(System.Net.Sockets.Socket,NetSharp.Packets.Packet,System.Net.Sockets.SocketFlags,System.TimeSpan)"> + <member name="M:NetSharp.Connection.DoSendPacketAsync(System.Net.Sockets.Socket,NetSharp.Packets.Packet,System.Net.Sockets.SocketFlags,System.TimeSpan,System.Threading.CancellationToken)"> <summary> - Sends the given packet asynchronously within the given timeout. + Sends the given packet to the network. </summary> <param name="remoteSocket">The remote socket to which to send the packet.</param> <param name="packet">The packet to send.</param> <param name="socketFlags">The socket flags associated with the write operation.</param> <param name="timeout"> - The timespan within which to send the packet, returning <c>false</c> if this limit is exceeded. + The timespan within which the packet should be received. After this timespan elapses, the send task is cancelled. </param> + <param name="cancellationToken">A pre-existing cancellation token that should be observed alongside the timeout.</param> <returns>Whether the packet was successfully sent.</returns> </member> - <member name="M:NetSharp.Connection.DoSendPacketToAsync(System.Net.Sockets.Socket,System.Net.EndPoint,NetSharp.Packets.Packet,System.Net.Sockets.SocketFlags,System.TimeSpan)"> + <member name="M:NetSharp.Connection.DoSendPacketToAsync(System.Net.Sockets.Socket,System.Net.EndPoint,NetSharp.Packets.Packet,System.Net.Sockets.SocketFlags,System.TimeSpan,System.Threading.CancellationToken)"> <summary> - Sends the given packet asynchronously within the given timeout. + Sends the given packet to the network. </summary> <param name="socket">The socket which should send the packet.</param> <param name="remoteEndPoint">The remote endpoint to which to send the packet.</param> <param name="packet">The packet to send.</param> <param name="socketFlags">The socket flags associated with the write operation.</param> <param name="timeout"> - The timespan within which to send the packet, returning <c>false</c> if this limit is exceeded. + The timespan within which the packet should be received. After this timespan elapses, the send task is cancelled. </param> + <param name="cancellationToken">A pre-existing cancellation token that should be observed alongside the timeout.</param> <returns>Whether the packet was successfully sent.</returns> </member> <member name="M:NetSharp.Connection.OnBytesReceived(System.Net.EndPoint,System.Int32)"> @@ -244,6 +233,16 @@ <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.Connection.BytesReceived"> + <summary> + Signifies that some data has been received from the remote endpoint. + </summary> + </member> + <member name="E:NetSharp.Connection.BytesSent"> + <summary> + Signifies that some data was sent to the remote endpoint. + </summary> + </member> <member name="M:NetSharp.Connection.ChangeLoggingStream(System.IO.Stream,NetSharp.Logging.LogLevel)"> <summary> Makes the client log to the given stream. @@ -708,21 +707,22 @@ The stream to which messages will be logged. </summary> </member> - <member name="F:NetSharp.Logging.Logger.writer"> + <member name="F:NetSharp.Logging.Logger.minimumSeverity"> <summary> - The text writer we will use to log messages to the underlying stream. + The minimum severity that log messages need to be logged to the underlying stream. </summary> </member> - <member name="F:NetSharp.Logging.Logger.minimumSeverity"> + <member name="F:NetSharp.Logging.Logger.writer"> <summary> - The minimum severity that log messages need to be logged to the underlying stream. + The text writer we will use to log messages to the underlying stream. </summary> </member> - <member name="M:NetSharp.Logging.Logger.#ctor(System.IO.Stream)"> + <member name="M:NetSharp.Logging.Logger.#ctor(System.IO.Stream,NetSharp.Logging.LogLevel)"> <summary> Initialises a new instance of the <see cref="T:NetSharp.Logging.Logger"/> struct. </summary> - <param name="streamToLogTo">The stream that the logger instance should log messages to.</param> + <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 /> @@ -807,12 +807,6 @@ </summary> <param name="message">The warning that should be logged.</param> </member> - <member name="M:NetSharp.Logging.Logger.SetMinimumLogSeverity(NetSharp.Logging.LogLevel)"> - <summary> - Sets the minimum severity level that new messages need to be logged to the underlying stream. - </summary> - <param name="minimumSeverityLevel">The new minimum severity level.</param> - </member> <member name="T:NetSharp.Packets.Builtin.ConnectPacket"> <summary> A simple connection request packet for the UDP protocol. @@ -1263,11 +1257,6 @@ Provides methods for handling connected <see cref="T:NetSharp.Interfaces.IClient"/> instances. </summary> </member> - <member name="F:NetSharp.Server.bindSocketCancellationTokenSource"> - <summary> - Cancellation token source for the <see cref="M:NetSharp.Server.TryBindAsync(System.Net.IPAddress,System.Int32)"/> method. - </summary> - </member> <member name="F:NetSharp.Server.complexPacketHandlers"> <summary> Maps a packet type id to the complex packet handler for that packet type. @@ -1279,95 +1268,84 @@ <see cref="T:NetSharp.Interfaces.IRequestPacket"/> implementors. </summary> </member> - <member name="F:NetSharp.Server.simplePacketHandlers"> - <summary> - Maps a packet type id to the simple packet handler for that packet type. - </summary> - </member> - <member name="F:NetSharp.Server.PendingConnectionBacklog"> - <summary> - The maximum number of connections that are allowed in the connection backlog. - </summary> - </member> - <member name="F:NetSharp.Server.DefaultNetworkOperationTimeout"> - <summary> - The default timeout value for all network operations. - </summary> - </member> <member name="F:NetSharp.Server.serverShutdownCancellationTokenSource"> <summary> Cancellation token source to stop handling client sockets when the server should be shut down. </summary> </member> - <member name="F:NetSharp.Server.socket"> + <member name="F:NetSharp.Server.simplePacketHandlers"> <summary> - The <see cref="T:System.Net.Sockets.Socket"/> underlying the connection. + Maps a packet type id to the simple packet handler for that packet type. </summary> </member> - <member name="F:NetSharp.Server.socketOptions"> + <member name="M:NetSharp.Server.#ctor"> <summary> - Backing field for the <see cref="P:NetSharp.Server.SocketOptions"/> property. + Initialises a new instance of the <see cref="T:NetSharp.Server"/> class. </summary> </member> - <member name="F:NetSharp.Server.runServer"> + <member name="M:NetSharp.Server.Finalize"> <summary> - Whether the server should be ran. + Destroys an instance of the <see cref="T:NetSharp.Server"/> class. </summary> </member> - <member name="M:NetSharp.Server.#ctor"> + <member name="T:NetSharp.Server.RawRequestPacketDeserialiser"> <summary> - Initialises a new instance of the <see cref="T:NetSharp.Server"/> class. + Represents a method that receives a raw packet, and deserialises it into an <see cref="T:NetSharp.Interfaces.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.Server.#ctor(System.Net.Sockets.SocketType,System.Net.Sockets.ProtocolType,NetSharp.Utils.Socket_Options.SocketOptionManager)"> + <member name="M:NetSharp.Server.RegisterInternalPacketHandlers"> <summary> - Initialises a new instance of the <see cref="T:NetSharp.Server"/> class. + Registers packet handlers for every internal library packet. </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="T:NetSharp.Utils.Socket_Options.SocketOptions"/> manager to use.</param> </member> - <member name="M:NetSharp.Server.Finalize"> + <member name="F:NetSharp.Server.PendingConnectionBacklog"> <summary> - Destroys an instance of the <see cref="T:NetSharp.Server"/> class. + The maximum number of connections that are allowed in the connection backlog. </summary> </member> - <member name="T:NetSharp.Server.RawRequestPacketDeserialiser"> + <member name="F:NetSharp.Server.DefaultNetworkOperationTimeout"> <summary> - Represents a method that receives a raw packet, and deserialises it into an <see cref="T:NetSharp.Interfaces.IRequestPacket"/> implementor. + The default timeout value for all network operations. </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="E:NetSharp.Server.ClientConnected"> + <member name="F:NetSharp.Server.serverShutdownCancellationToken"> <summary> - Signifies that a connection with a remote endpoint has been made. + The cancellation token that will be set when the server must be shut down. </summary> </member> - <member name="E:NetSharp.Server.ClientDisconnected"> + <member name="F:NetSharp.Server.socket"> <summary> - Signifies that a connection with a remote endpoint has been lost. + The <see cref="T:System.Net.Sockets.Socket"/> underlying the connection. </summary> </member> - <member name="E:NetSharp.Server.ServerStarted"> + <member name="F:NetSharp.Server.socketOptions"> <summary> - Signifies that the server was started and clients will start being accepted. + Backing field for the <see cref="P:NetSharp.Server.SocketOptions"/> property. </summary> </member> - <member name="E:NetSharp.Server.ServerStopped"> + <member name="F:NetSharp.Server.runServer"> <summary> - Signifies that the server was stopped and clients will stop being accepted. + Whether the server should be ran. </summary> </member> - <member name="P:NetSharp.Server.SocketOptions"> + <member name="M:NetSharp.Server.#ctor(System.Net.Sockets.SocketType,System.Net.Sockets.ProtocolType,NetSharp.Utils.Socket_Options.SocketOptionManager)"> <summary> - The configured socket options for the underlying connection. + Initialises a new instance of the <see cref="T:NetSharp.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="T:NetSharp.Utils.Socket_Options.SocketOptions"/> manager to use.</param> </member> - <member name="M:NetSharp.Server.RegisterInternalPacketHandlers"> + <member name="M:NetSharp.Server.#ctor(System.Net.Sockets.SocketType,System.Net.Sockets.ProtocolType,NetSharp.Utils.Socket_Options.SocketOptionManager,System.TimeSpan)"> <summary> - Registers packet handlers for every internal library packet. + Initialises a new instance of the <see cref="T:NetSharp.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="T:NetSharp.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.Server.DeserialiseRequestPacket(System.UInt32,NetSharp.Packets.Packet@)"> <summary> @@ -1389,11 +1367,12 @@ </summary> <param name="clientHandlerArgsObj">The object representing the passed <see cref="T:NetSharp.Server.ClientHandlerArgs"/> instance.</param> </member> - <member name="M:NetSharp.Server.HandleClientAsync(NetSharp.Server.ClientHandlerArgs)"> + <member name="M:NetSharp.Server.HandleClientAsync(NetSharp.Server.ClientHandlerArgs,System.Threading.CancellationToken)"> <summary> Handles a new 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.Server.HandleRequestPacket(System.UInt32,NetSharp.Interfaces.IRequestPacket@,System.Net.EndPoint@)"> <summary> @@ -1481,29 +1460,18 @@ <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.Server.RunAsync(System.Net.EndPoint)"> - <inheritdoc /> - </member> - <member name="M:NetSharp.Server.Shutdown"> - <inheritdoc /> - </member> - <member name="M:NetSharp.Server.TryDeregisterComplexPacketHandler``2(NetSharp.ComplexPacketHandler{``0,``1}@)"> - <inheritdoc /> - </member> - <member name="M:NetSharp.Server.TryDeregisterSimplePacketHandler``1(NetSharp.SimplePacketHandler{``0}@)"> - <inheritdoc /> - </member> - <member name="M:NetSharp.Server.TryRegisterComplexPacketHandler``2(NetSharp.ComplexPacketHandler{``0,``1})"> - <inheritdoc /> - </member> - <member name="M:NetSharp.Server.TryRegisterSimplePacketHandler``1(NetSharp.SimplePacketHandler{``0})"> - <inheritdoc /> - </member> <member name="T:NetSharp.Server.ClientHandlerArgs"> <summary> Holds information about the arguments passed to every client handler task. </summary> </member> + <member name="M:NetSharp.Server.ClientHandlerArgs.#ctor(System.Net.EndPoint,System.Net.Sockets.Socket)"> + <summary> + Initialises a new instance of the <see cref="T:NetSharp.Server.ClientHandlerArgs"/> struct. + </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.Server.ClientHandlerArgs.ClientEndPoint"> <summary> The remote endpoint for the client being handled. @@ -1514,13 +1482,6 @@ The client handler socket for the client being handled. Is only set if using TCP. </summary> </member> - <member name="M:NetSharp.Server.ClientHandlerArgs.#ctor(System.Net.EndPoint,System.Net.Sockets.Socket)"> - <summary> - Initialises a new instance of the <see cref="T:NetSharp.Server.ClientHandlerArgs"/> struct. - </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="M:NetSharp.Server.ClientHandlerArgs.ForTcpClientHandler(System.Net.Sockets.Socket@)"> <summary> Constructs a new instance of the <see cref="T:NetSharp.Server.ClientHandlerArgs"/> for a TCP client. @@ -1533,15 +1494,66 @@ </summary> <returns>A new instance of the <see cref="T:NetSharp.Server.ClientHandlerArgs"/>, setup for a UDP client.</returns> </member> + <member name="E:NetSharp.Server.ClientConnected"> + <summary> + Signifies that a connection with a remote endpoint has been made. + </summary> + </member> + <member name="E:NetSharp.Server.ClientDisconnected"> + <summary> + Signifies that a connection with a remote endpoint has been lost. + </summary> + </member> + <member name="E:NetSharp.Server.ServerStarted"> + <summary> + Signifies that the server was started and clients will start being accepted. + </summary> + </member> + <member name="E:NetSharp.Server.ServerStopped"> + <summary> + Signifies that the server was stopped and clients will stop being accepted. + </summary> + </member> + <member name="P:NetSharp.Server.NetworkOperationTimeout"> + <summary> + The timeout value for network operations such as sending bytes or receiving bytes over the network. + </summary> + </member> + <member name="P:NetSharp.Server.SocketOptions"> + <summary> + The configured socket options for the underlying connection. + </summary> + </member> + <member name="M:NetSharp.Server.RunAsync(System.Net.EndPoint)"> + <inheritdoc /> + </member> + <member name="M:NetSharp.Server.Shutdown"> + <inheritdoc /> + </member> + <member name="M:NetSharp.Server.TryDeregisterComplexPacketHandler``2(NetSharp.ComplexPacketHandler{``0,``1}@)"> + <inheritdoc /> + </member> + <member name="M:NetSharp.Server.TryDeregisterSimplePacketHandler``1(NetSharp.SimplePacketHandler{``0}@)"> + <inheritdoc /> + </member> + <member name="M:NetSharp.Server.TryRegisterComplexPacketHandler``2(NetSharp.ComplexPacketHandler{``0,``1})"> + <inheritdoc /> + </member> + <member name="M:NetSharp.Server.TryRegisterSimplePacketHandler``1(NetSharp.SimplePacketHandler{``0})"> + <inheritdoc /> + </member> <member name="T:NetSharp.Servers.TcpServer"> <summary> Provides methods for TCP communication with connected <see cref="T:NetSharp.Clients.TcpClient"/> instances. </summary> </member> - <member name="M:NetSharp.Servers.TcpServer.#ctor"> + <member name="M:NetSharp.Servers.TcpServer.HandleClientAsync(NetSharp.Server.ClientHandlerArgs,System.Threading.CancellationToken)"> + <inheritdoc /> + </member> + <member name="M:NetSharp.Servers.TcpServer.#ctor(System.TimeSpan)"> <inheritdoc /> </member> - <member name="M:NetSharp.Servers.TcpServer.HandleClientAsync(NetSharp.Server.ClientHandlerArgs)"> + <member name="M:NetSharp.Servers.TcpServer.#ctor"> <inheritdoc /> </member> <member name="M:NetSharp.Servers.TcpServer.RunAsync(System.Net.EndPoint)"> @@ -1552,20 +1564,23 @@ Provides methods for UDP communication with connected <see cref="T:NetSharp.Clients.UdpClient"/> instances. </summary> </member> - <member name="F:NetSharp.Servers.UdpServer.activeClients"> + <member name="F:NetSharp.Servers.UdpServer.clientChannelOptions"> <summary> - Holds currently connected and active clients, as well as their current received packet queues. + The options that should be applied to every channel created to handle a client. </summary> </member> - <member name="F:NetSharp.Servers.UdpServer.clientChannelOptions"> + <member name="F:NetSharp.Servers.UdpServer.activeClients"> <summary> - The options that should be applied to every channel created to handle a client. + Holds currently connected and active clients, as well as their current received packet queues. </summary> </member> - <member name="M:NetSharp.Servers.UdpServer.#ctor"> + <member name="M:NetSharp.Servers.UdpServer.HandleClientAsync(NetSharp.Server.ClientHandlerArgs,System.Threading.CancellationToken)"> <inheritdoc /> </member> - <member name="M:NetSharp.Servers.UdpServer.HandleClientAsync(NetSharp.Server.ClientHandlerArgs)"> + <member name="M:NetSharp.Servers.UdpServer.#ctor(System.TimeSpan)"> + <inheritdoc /> + </member> + <member name="M:NetSharp.Servers.UdpServer.#ctor"> <inheritdoc /> </member> <member name="M:NetSharp.Servers.UdpServer.RunAsync(System.Net.EndPoint)"> @@ -1700,11 +1715,6 @@ The default port over which a connection is made. </summary> </member> - <member name="F:NetSharp.Utils.Constants.UdpMaxBufferSize"> - <summary> - The largest byte buffer that can be sent via UDP. - </summary> - </member> <member name="T:NetSharp.Utils.Conversion.EndianAwareBitConverter"> <summary> Wraps the <see cref="T:System.BitConverter"/> class to provide conversion that is endian-aware. @@ -1780,99 +1790,85 @@ Helper class for asynchronously performing common network operations, for both the UDP and TCP protocols. </summary> </member> - <member name="M:NetSharp.Utils.NetworkOperations.ReadAsync(System.Net.Sockets.Socket,System.Int32,System.Net.Sockets.SocketFlags,System.Threading.CancellationToken)"> + <member name="M:NetSharp.Utils.NetworkOperations.Read(System.Net.Sockets.Socket,System.Int32,System.Net.Sockets.SocketFlags)"> <summary> - Reads the specified amount of data asynchronously from the network, via the given socket. - The given <see cref="T:System.Net.Sockets.SocketFlags"/> are associated with the read, and the given <see cref="T:System.Threading.CancellationToken"/> - is used to allow for asynchronous task cancellation. + Reads the specified amount of data from the network, via the given socket. + The given <see cref="T:System.Net.Sockets.SocketFlags"/> are associated with the read. </summary> <param name="socket">The socket which should read data from the network.</param> <param name="count">The number of bytes to read from the network.</param> <param name="socketFlags">The socket flags associated with the receive operation.</param> - <param name="cancellationToken">The cancellation token to use for asynchronous cancellation.</param> <returns>The result of the receive operation.</returns> </member> - <member name="M:NetSharp.Utils.NetworkOperations.ReadFromAsync(System.Net.Sockets.Socket,System.Int32,System.Net.EndPoint,System.Net.Sockets.SocketFlags,System.Threading.CancellationToken)"> + <member name="M:NetSharp.Utils.NetworkOperations.ReadFrom(System.Net.Sockets.Socket,System.Int32,System.Net.EndPoint,System.Net.Sockets.SocketFlags)"> <summary> - Reads a datagram asynchronously from the given remote endpoint, via the given socket. - The given <see cref="T:System.Net.Sockets.SocketFlags"/> are associated with the read, and the given <see cref="T:System.Threading.CancellationToken"/> - is used to allow for asynchronous task cancellation. + Reads a datagram segment of the given length from the given remote endpoint, via the given socket. + The given <see cref="T:System.Net.Sockets.SocketFlags"/> are associated with the read. </summary> <param name="socket">The socket which should read data from the network.</param> <param name="count">The number of bytes to read from the network.</param> <param name="remoteEndPoint">The remote endpoint from which data should be read.</param> <param name="socketFlags">The socket flags associated with the receive operation.</param> - <param name="cancellationToken">The cancellation token to use for asynchronous cancellation.</param> <returns>The result of the receive operation.</returns> </member> - <member name="M:NetSharp.Utils.NetworkOperations.ReadPacketAsync(System.Net.Sockets.Socket,System.Net.Sockets.SocketFlags,System.Threading.CancellationToken)"> + <member name="M:NetSharp.Utils.NetworkOperations.Write(System.Net.Sockets.Socket,System.ReadOnlyMemory{System.Byte},System.Net.Sockets.SocketFlags)"> <summary> - Reads a packet asynchronously from network, via the given socket. The given <see cref="T:System.Net.Sockets.SocketFlags"/> are - associated with the read, and the given <see cref="T:System.Threading.CancellationToken"/> is used to allow for asynchronous - task cancellation. + Writes the given data buffer to the network, via the given socket. + The given <see cref="T:System.Net.Sockets.SocketFlags"/> are associated with the write, and the given <see cref="T:System.Threading.CancellationToken"/> + is used to allow for asynchronous task cancellation. + </summary> + <param name="socket">The socket which should write data to the network.</param> + <param name="buffer">The buffer that should be written to the network.</param> + <param name="socketFlags">The socket flags associated with the send operation.</param> + </member> + <member name="M:NetSharp.Utils.NetworkOperations.WriteTo(System.Net.Sockets.Socket,System.Net.EndPoint,System.ReadOnlyMemory{System.Byte},System.Net.Sockets.SocketFlags)"> + <summary> + Writes the given data buffer to the given remote endpoint, via the given socket. + The given <see cref="T:System.Net.Sockets.SocketFlags"/> are associated with the write, and the given <see cref="T:System.Threading.CancellationToken"/> + is used to allow for asynchronous task cancellation. + </summary> + <param name="socket">The socket which should write data to the network.</param> + <param name="remoteEndPoint">The remote endpoint to which data should be written.</param> + <param name="buffer">The buffer that should be written to the network.</param> + <param name="socketFlags">The socket flags associated with the send operation.</param> + </member> + <member name="M:NetSharp.Utils.NetworkOperations.ReadPacket(System.Net.Sockets.Socket,System.Net.Sockets.SocketFlags)"> + <summary> + Reads a packet from network, via the given socket. + The given <see cref="T:System.Net.Sockets.SocketFlags"/> are associated with the read. </summary> <param name="socket">The socket which should read the packet from the network.</param> <param name="socketFlags">The socket flags associated with the receive operation.</param> - <param name="cancellationToken">The cancellation token to use for asynchronous cancellation.</param> <returns>The read packet.</returns> </member> - <member name="M:NetSharp.Utils.NetworkOperations.ReadPacketFromAsync(System.Net.Sockets.Socket,System.Net.EndPoint,System.Net.Sockets.SocketFlags,System.Threading.CancellationToken)"> + <member name="M:NetSharp.Utils.NetworkOperations.ReadPacketFrom(System.Net.Sockets.Socket,System.Net.EndPoint,System.Net.Sockets.SocketFlags)"> <summary> - Reads a packet asynchronously from the given remote endpoint, via the given socket. The given - <see cref="T:System.Net.Sockets.SocketFlags"/> are associated with the read, and the given <see cref="T:System.Threading.CancellationToken"/> is - used to allow for asynchronous task cancellation. + Reads a packet from the given remote endpoint, via the given socket. + The given <see cref="T:System.Net.Sockets.SocketFlags"/> are associated with the read. </summary> <param name="socket">The socket which should read the packet from the network.</param> <param name="remoteEndPoint">The remote endpoint from which a packet should be read.</param> <param name="socketFlags">The socket flags associated with the receive operation.</param> - <param name="cancellationToken">The cancellation token to use for asynchronous cancellation.</param> <returns>The read packet and associated transmission results.</returns> </member> - <member name="M:NetSharp.Utils.NetworkOperations.WriteAsync(System.Net.Sockets.Socket,System.ReadOnlyMemory{System.Byte},System.Net.Sockets.SocketFlags,System.Threading.CancellationToken)"> + <member name="M:NetSharp.Utils.NetworkOperations.WritePacket(System.Net.Sockets.Socket,NetSharp.Packets.Packet,System.Net.Sockets.SocketFlags)"> <summary> - Writes the given buffer asynchronously to the network, via the given socket. - The given <see cref="T:System.Net.Sockets.SocketFlags"/> are associated with the write, and the given <see cref="T:System.Threading.CancellationToken"/> - is used to allow for asynchronous task cancellation. - </summary> - <param name="socket">The socket which should write data to the network.</param> - <param name="buffer">The buffer that should be written to the network.</param> - <param name="socketFlags">The socket flags associated with the send operation.</param> - <param name="cancellationToken">The cancellation token to use for asynchronous cancellation.</param> - </member> - <member name="M:NetSharp.Utils.NetworkOperations.WritePacketAsync(System.Net.Sockets.Socket,NetSharp.Packets.Packet,System.Net.Sockets.SocketFlags,System.Threading.CancellationToken)"> - <summary> - Writes the given packet asynchronously to the network, via the given socket. The given <see cref="T:System.Net.Sockets.SocketFlags"/> - are associated with the write, and the given <see cref="T:System.Threading.CancellationToken"/> is used to allow for asynchronous - task cancellation. + Writes the given packet to the network, via the given socket. + The given <see cref="T:System.Net.Sockets.SocketFlags"/> are associated with the write. </summary> <param name="socket">The socket which should write data to the network.</param> <param name="packet">The packet that should be written to the network.</param> <param name="socketFlags">The socket flags associated with the send operation.</param> - <param name="cancellationToken">The cancellation token to use for asynchronous cancellation.</param> </member> - <member name="M:NetSharp.Utils.NetworkOperations.WritePacketToAsync(System.Net.Sockets.Socket,System.Net.EndPoint,NetSharp.Packets.Packet,System.Net.Sockets.SocketFlags,System.Threading.CancellationToken)"> + <member name="M:NetSharp.Utils.NetworkOperations.WritePacketTo(System.Net.Sockets.Socket,System.Net.EndPoint,NetSharp.Packets.Packet,System.Net.Sockets.SocketFlags)"> <summary> - Writes the given packet asynchronously to the given remote endpoint, via the given socket. - The given <see cref="T:System.Net.Sockets.SocketFlags"/> are associated with the write, and the given <see cref="T:System.Threading.CancellationToken"/> - is used to allow for asynchronous task cancellation. + Writes the given packet to the given remote endpoint, via the given socket. + The given <see cref="T:System.Net.Sockets.SocketFlags"/> are associated with the write. </summary> <param name="socket">The socket which should write data to the network.</param> <param name="remoteEndPoint">The remote endpoint to which data should be written.</param> <param name="packet">The packet that should be written to the remote endpoint.</param> <param name="socketFlags">The socket flags associated with the send operation.</param> - <param name="cancellationToken">The cancellation token to use for asynchronous cancellation.</param> - </member> - <member name="M:NetSharp.Utils.NetworkOperations.WriteToAsync(System.Net.Sockets.Socket,System.Net.EndPoint,System.ReadOnlyMemory{System.Byte},System.Net.Sockets.SocketFlags,System.Threading.CancellationToken)"> - <summary> - Writes the given buffer asynchronously to the given remote endpoint, via the given socket. - The given <see cref="T:System.Net.Sockets.SocketFlags"/> are associated with the write, and the given <see cref="T:System.Threading.CancellationToken"/> - is used to allow for asynchronous task cancellation. - </summary> - <param name="socket">The socket which should write data to the network.</param> - <param name="remoteEndPoint">The remote endpoint to which data should be written.</param> - <param name="buffer">The buffer that should be written to the network.</param> - <param name="socketFlags">The socket flags associated with the send operation.</param> - <param name="cancellationToken">The cancellation token to use for asynchronous cancellation.</param> </member> <member name="T:NetSharp.Utils.Socket_Options.DefaultSocketOptions"> <summary> diff --git a/NetSharp/NetSharp/Packets/Packet.cs b/NetSharp/NetSharp/Packets/Packet.cs @@ -63,7 +63,7 @@ namespace NetSharp.Packets { Span<byte> serialisedType = buffer.Slice(sizeof(int), sizeof(uint)).Span; Span<byte> serialisedErrorCode = buffer.Slice(sizeof(int) + sizeof(uint), sizeof(uint)).Span; - Memory<byte> serialisedData = buffer.Slice(HeaderSize); + ReadOnlyMemory<byte> serialisedData = buffer.Slice(HeaderSize); return new Packet(serialisedData, EndianAwareBitConverter.ToUInt32(serialisedType), diff --git a/NetSharp/NetSharp/Server.cs b/NetSharp/NetSharp/Server.cs @@ -40,11 +40,6 @@ namespace NetSharp public abstract class Server : Connection, IServer, IDisposable { /// <summary> - /// Cancellation token source for the <see cref="TryBindAsync(IPAddress,int)"/> method. - /// </summary> - private readonly CancellationTokenSource bindSocketCancellationTokenSource; - - /// <summary> /// Maps a packet type id to the complex packet handler for that packet type. /// </summary> private readonly ConcurrentDictionary<uint, Func<IRequestPacket, EndPoint, IResponsePacket<IRequestPacket>>> @@ -57,39 +52,14 @@ namespace NetSharp private readonly ConcurrentDictionary<uint, RawRequestPacketDeserialiser> requestPacketDeserialisers; /// <summary> - /// Maps a packet type id to the simple packet handler for that packet type. - /// </summary> - private readonly ConcurrentDictionary<uint, Action<IRequestPacket, EndPoint>> simplePacketHandlers; - - /// <summary> - /// The maximum number of connections that are allowed in the connection backlog. - /// </summary> - protected const int PendingConnectionBacklog = 100; - - /// <summary> - /// The default timeout value for all network operations. - /// </summary> - protected static readonly TimeSpan DefaultNetworkOperationTimeout = TimeSpan.FromMilliseconds(10_000); - - /// <summary> /// Cancellation token source to stop handling client sockets when the server should be shut down. /// </summary> - protected readonly CancellationTokenSource serverShutdownCancellationTokenSource; + private readonly CancellationTokenSource serverShutdownCancellationTokenSource; /// <summary> - /// The <see cref="Socket"/> underlying the connection. - /// </summary> - protected readonly Socket socket; - - /// <summary> - /// Backing field for the <see cref="SocketOptions"/> property. - /// </summary> - protected readonly SocketOptions socketOptions; - - /// <summary> - /// Whether the server should be ran. + /// Maps a packet type id to the simple packet handler for that packet type. /// </summary> - protected volatile bool runServer; + private readonly ConcurrentDictionary<uint, Action<IRequestPacket, EndPoint>> simplePacketHandlers; /// <summary> /// Initialises a new instance of the <see cref="Server"/> class. @@ -97,7 +67,8 @@ namespace NetSharp private Server() { serverShutdownCancellationTokenSource = new CancellationTokenSource(); - bindSocketCancellationTokenSource = new CancellationTokenSource(); + + serverShutdownCancellationToken = serverShutdownCancellationTokenSource.Token; requestPacketDeserialisers = new ConcurrentDictionary<uint, RawRequestPacketDeserialiser>(); @@ -112,24 +83,6 @@ namespace NetSharp } /// <summary> - /// Initialises a new instance of the <see cref="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> - protected Server(SocketType socketType, ProtocolType protocolType, SocketOptionManager socketManager) : this() - { - socket = new Socket(AddressFamily.InterNetwork, socketType, protocolType); - - socketOptions = socketManager switch - { - SocketOptionManager.Tcp => new TcpSocketOptions(ref socket) as SocketOptions, - SocketOptionManager.Udp => new UdpSocketOptions(ref socket) as SocketOptions, - _ => new DefaultSocketOptions(ref socket), - }; - } - - /// <summary> /// Destroys an instance of the <see cref="Server"/> class. /// </summary> ~Server() @@ -145,35 +98,6 @@ namespace NetSharp private delegate IRequestPacket RawRequestPacketDeserialiser(in Packet rawPacket); /// <summary> - /// Signifies that a connection with a remote endpoint has been made. - /// </summary> - public event Action<EndPoint>? ClientConnected; - - //protected IResponsePacket<IRequestPacket> DeserialiseResponsePacket(in Packet) - /// <summary> - /// Signifies that a connection with a remote endpoint has been lost. - /// </summary> - public event Action<EndPoint>? ClientDisconnected; - - /// <summary> - /// Signifies that the server was started and clients will start being accepted. - /// </summary> - public event Action? ServerStarted; - - /// <summary> - /// Signifies that the server was stopped and clients will stop being accepted. - /// </summary> - public event Action? ServerStopped; - - /// <summary> - /// The configured socket options for the underlying connection. - /// </summary> - public SocketOptions SocketOptions - { - get { return socketOptions; } - } - - /// <summary> /// Registers packet handlers for every internal library packet. /// </summary> private void RegisterInternalPacketHandlers() @@ -211,6 +135,69 @@ namespace NetSharp } /// <summary> + /// The maximum number of connections that are allowed in the connection backlog. + /// </summary> + protected const int PendingConnectionBacklog = 100; + + /// <summary> + /// The default timeout value for all network operations. + /// </summary> + protected static readonly TimeSpan DefaultNetworkOperationTimeout = TimeSpan.FromMilliseconds(10_000); + + /// <summary> + /// The cancellation token that will be set when the server must be shut down. + /// </summary> + protected readonly CancellationToken serverShutdownCancellationToken; + + /// <summary> + /// The <see cref="Socket"/> underlying the connection. + /// </summary> + protected readonly Socket socket; + + /// <summary> + /// Backing field for the <see cref="SocketOptions"/> property. + /// </summary> + protected readonly SocketOptions socketOptions; + + /// <summary> + /// Whether the server should be ran. + /// </summary> + protected volatile bool runServer; + + /// <summary> + /// Initialises a new instance of the <see cref="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> + protected Server(SocketType socketType, ProtocolType protocolType, SocketOptionManager socketManager) + : this(socketType, protocolType, socketManager, DefaultNetworkOperationTimeout) + { + } + + /// <summary> + /// Initialises a new instance of the <see cref="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> + protected Server(SocketType socketType, ProtocolType protocolType, SocketOptionManager socketManager, + TimeSpan networkOperationTimeout) : this() + { + socket = new Socket(AddressFamily.InterNetwork, socketType, protocolType); + + socketOptions = socketManager switch + { + SocketOptionManager.Tcp => new TcpSocketOptions(ref socket) as SocketOptions, + SocketOptionManager.Udp => new UdpSocketOptions(ref socket) as SocketOptions, + _ => new DefaultSocketOptions(ref socket), + }; + + NetworkOperationTimeout = networkOperationTimeout; + } + + /// <summary> /// Deserialises the given <see cref="Packet"/> struct into an <see cref="IRequestPacket"/> implementor. /// </summary> /// <param name="packetType">The type id of packet that we should deserialise to.</param> @@ -237,8 +224,8 @@ namespace NetSharp { if (disposing) { - bindSocketCancellationTokenSource?.Cancel(); - bindSocketCancellationTokenSource?.Dispose(); + serverShutdownCancellationTokenSource?.Cancel(); + serverShutdownCancellationTokenSource?.Dispose(); socket.Dispose(); } @@ -256,10 +243,10 @@ namespace NetSharp try { - await HandleClientAsync(clientHandlerArgs); + await HandleClientAsync(clientHandlerArgs, serverShutdownCancellationTokenSource.Token); } - catch (TaskCanceledException) { logger.LogMessage("Client handling was cancelled via a task cancellation."); } - catch (OperationCanceledException) { logger.LogMessage("Client handling was cancelled via an operation cancellation."); } + catch (TaskCanceledException) { logger.LogWarning("Client handling was cancelled via a task cancellation."); } + catch (OperationCanceledException) { logger.LogWarning("Client handling was cancelled via an operation cancellation."); } catch (Exception ex) { logger.LogException("Exception during client handling", ex); @@ -282,7 +269,8 @@ namespace NetSharp /// Handles a new client asynchronously. /// </summary> /// <param name="args">The client handler arguments that should be passed to the client handler.</param> - protected abstract Task HandleClientAsync(ClientHandlerArgs args); + /// <param name="cancellationToken">Cancellation token set when the server is shutting down.</param> + protected abstract Task HandleClientAsync(ClientHandlerArgs args, CancellationToken cancellationToken); /// <summary> /// Handles the given request packet with a registered packet handler. In this case, a complex packet handler @@ -312,9 +300,9 @@ namespace NetSharp return null; } - #if DEBUG +#if DEBUG logger.LogWarning($"No packet handler was registered for packet of type {packetType}"); - #endif +#endif } catch (Exception ex) { @@ -411,16 +399,18 @@ namespace NetSharp /// <returns>Whether the binding was successful or not.</returns> protected async Task<bool> TryBindAsync(EndPoint localEndPoint, TimeSpan timeout) { + CancellationTokenSource timeoutCancellationTokenSource = new CancellationTokenSource(timeout); + CancellationTokenSource cts = + CancellationTokenSource.CreateLinkedTokenSource(timeoutCancellationTokenSource.Token, serverShutdownCancellationToken); + try { - bindSocketCancellationTokenSource.CancelAfter(timeout); - return await Task.Run(() => { socket.Bind(localEndPoint); return true; - }, bindSocketCancellationTokenSource.Token); + }, cts.Token); } catch (TaskCanceledException) { @@ -431,6 +421,91 @@ namespace NetSharp logger.LogException($"Socket exception on binding socket to {localEndPoint}:", ex); return false; } + finally + { + cts.Dispose(); + timeoutCancellationTokenSource.Dispose(); + } + } + + /// <summary> + /// Holds information about the arguments passed to every client handler task. + /// </summary> + protected readonly struct ClientHandlerArgs + { + /// <summary> + /// Initialises a new instance of the <see cref="ClientHandlerArgs"/> struct. + /// </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> + private ClientHandlerArgs(EndPoint remoteEndPoint, Socket? handlerSocket) + { + ClientEndPoint = remoteEndPoint; + + ClientSocket = handlerSocket; + } + + /// <summary> + /// The remote endpoint for the client being handled. + /// </summary> + public readonly EndPoint ClientEndPoint; + + /// <summary> + /// The client handler socket for the client being handled. Is only set if using TCP. + /// </summary> + public readonly Socket? ClientSocket; + + /// <summary> + /// Constructs a new instance of the <see cref="ClientHandlerArgs"/> for a TCP client. + /// </summary> + /// <returns>A new instance of the <see cref="ClientHandlerArgs"/>, setup for a TCP client.</returns> + public static ClientHandlerArgs ForTcpClientHandler(in Socket clientHandlerSocket) + { + return new ClientHandlerArgs(clientHandlerSocket.RemoteEndPoint, clientHandlerSocket); + } + + /// <summary> + /// Constructs a new instance of the <see cref="ClientHandlerArgs"/> for a UDP client. + /// </summary> + /// <returns>A new instance of the <see cref="ClientHandlerArgs"/>, setup for a UDP client.</returns> + public static ClientHandlerArgs ForUdpClientHandler(in EndPoint clientEndPoint) + { + return new ClientHandlerArgs(clientEndPoint, null); + } + } + + /// <summary> + /// Signifies that a connection with a remote endpoint has been made. + /// </summary> + public event Action<EndPoint>? ClientConnected; + + //protected IResponsePacket<IRequestPacket> DeserialiseResponsePacket(in Packet) + /// <summary> + /// Signifies that a connection with a remote endpoint has been lost. + /// </summary> + public event Action<EndPoint>? ClientDisconnected; + + /// <summary> + /// Signifies that the server was started and clients will start being accepted. + /// </summary> + public event Action? ServerStarted; + + /// <summary> + /// Signifies that the server was stopped and clients will stop being accepted. + /// </summary> + public event Action? ServerStopped; + + /// <summary> + /// The timeout value for network operations such as sending bytes or receiving bytes over the network. + /// </summary> + public TimeSpan NetworkOperationTimeout { get; protected set; } + + /// <summary> + /// The configured socket options for the underlying connection. + /// </summary> + public SocketOptions SocketOptions + { + get { return socketOptions; } } /// <inheritdoc /> @@ -572,51 +647,5 @@ namespace NetSharp return false; } - - /// <summary> - /// Holds information about the arguments passed to every client handler task. - /// </summary> - protected readonly struct ClientHandlerArgs - { - /// <summary> - /// The remote endpoint for the client being handled. - /// </summary> - public readonly EndPoint ClientEndPoint; - - /// <summary> - /// The client handler socket for the client being handled. Is only set if using TCP. - /// </summary> - public readonly Socket? ClientSocket; - - /// <summary> - /// Initialises a new instance of the <see cref="ClientHandlerArgs"/> struct. - /// </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> - private ClientHandlerArgs(EndPoint remoteEndPoint, Socket? handlerSocket) - { - ClientEndPoint = remoteEndPoint; - - ClientSocket = handlerSocket; - } - - /// <summary> - /// Constructs a new instance of the <see cref="ClientHandlerArgs"/> for a TCP client. - /// </summary> - /// <returns>A new instance of the <see cref="ClientHandlerArgs"/>, setup for a TCP client.</returns> - public static ClientHandlerArgs ForTcpClientHandler(in Socket clientHandlerSocket) - { - return new ClientHandlerArgs(clientHandlerSocket.RemoteEndPoint, clientHandlerSocket); - } - - /// <summary> - /// Constructs a new instance of the <see cref="ClientHandlerArgs"/> for a UDP client. - /// </summary> - /// <returns>A new instance of the <see cref="ClientHandlerArgs"/>, setup for a UDP client.</returns> - public static ClientHandlerArgs ForUdpClientHandler(in EndPoint clientEndPoint) - { - return new ClientHandlerArgs(clientEndPoint, null); - } - } } } \ No newline at end of file diff --git a/NetSharp/NetSharp/Servers/TcpServer.cs b/NetSharp/NetSharp/Servers/TcpServer.cs @@ -17,15 +17,9 @@ namespace NetSharp.Servers public sealed class TcpServer : Server { /// <inheritdoc /> - public TcpServer() : base(SocketType.Stream, ProtocolType.Tcp, SocketOptionManager.Tcp) - { - } - - /// <inheritdoc /> - protected override async Task HandleClientAsync(ClientHandlerArgs args) + protected override async Task HandleClientAsync(ClientHandlerArgs args, CancellationToken cancellationToken) { Socket clientHandlerSocket = args.ClientSocket ?? new Socket(SocketType.Unknown, ProtocolType.Unknown); - EndPoint remoteEp = clientHandlerSocket.RemoteEndPoint; logger.LogMessage($"Initialised client handler for client socket: [Remote EP: {remoteEp}]"); @@ -35,12 +29,14 @@ namespace NetSharp.Servers do { // receive a single raw packet from the network - Packet rawRequest = - await DoReceivePacketAsync(clientHandlerSocket, SocketFlags.None, Timeout.InfiniteTimeSpan); + Packet rawRequest = await DoReceivePacketAsync(clientHandlerSocket, SocketFlags.None, + Timeout.InfiniteTimeSpan, cancellationToken); - if (rawRequest.Equals(NullPacket) || rawRequest.Type == PacketRegistry.GetPacketId<DisconnectPacket>()) + if (rawRequest.Equals(NullPacket) || + rawRequest.Type == PacketRegistry.GetPacketId<DisconnectPacket>()) { - logger.LogMessage($"Received a disconnect packet from client socket: [Remote EP: {remoteEp}]"); + logger.LogMessage( + $"Received a disconnect packet from client socket: [Remote EP: {remoteEp}]"); break; } @@ -65,39 +61,49 @@ namespace NetSharp.Servers if (responsePacketType == null) { - logger.LogError($"Response packet type for request packet of type {requestPacketType} is null"); + logger.LogError( + $"Response packet type for request packet of type {requestPacketType} is null"); continue; } uint responsePacketTypeId = PacketRegistry.GetPacketId(responsePacketType); responsePacket.BeforeSerialisation(); - Packet rawResponse = new Packet(responsePacket.Serialise(), responsePacketTypeId, NetworkErrorCode.Ok); + Packet rawResponse = new Packet(responsePacket.Serialise(), responsePacketTypeId, + NetworkErrorCode.Ok); // echo back the processed raw response to the network - bool sentCorrectly = - await DoSendPacketAsync(clientHandlerSocket, rawResponse, SocketFlags.None, DefaultNetworkOperationTimeout); + bool sentCorrectly = await DoSendPacketAsync(clientHandlerSocket, rawResponse, SocketFlags.None, + NetworkOperationTimeout, cancellationToken); if (!sentCorrectly) { - logger.LogMessage($"Could not send response back to client socket: [Remote EP: {remoteEp}]"); + logger.LogMessage( + $"Could not send response back to client socket: [Remote EP: {remoteEp}]"); break; } logger.LogMessage($"Sent {rawResponse.TotalSize} bytes to {remoteEp}"); } while (true); - - logger.LogMessage($"Stopping client handler for client socket: [Remote EP: {remoteEp}]"); } - catch (TaskCanceledException) { logger.LogMessage("Client handling was cancelled via a task cancellation."); } - catch (OperationCanceledException) { logger.LogMessage("Client handling was cancelled via an operation cancellation."); } - catch (Exception ex) + finally { - logger.LogException("Exception during client socket handling:", ex); + logger.LogMessage($"Stopping client handler for client socket: [Remote EP: {remoteEp}]"); } } /// <inheritdoc /> + public TcpServer(TimeSpan networkOperationTimeout) : base(SocketType.Stream, ProtocolType.Tcp, + SocketOptionManager.Tcp, networkOperationTimeout) + { + } + + /// <inheritdoc /> + public TcpServer() : this(DefaultNetworkOperationTimeout) + { + } + + /// <inheritdoc /> public override async Task RunAsync(EndPoint localEndPoint) { bool bound = await TryBindAsync(localEndPoint); @@ -120,10 +126,8 @@ namespace NetSharp.Servers Socket clientSocket = await socket.AcceptAsync(); ClientHandlerArgs args = ClientHandlerArgs.ForTcpClientHandler(in clientSocket); - await Task.Factory.StartNew(DoHandleClientAsync, args, - serverShutdownCancellationTokenSource.Token, - TaskCreationOptions.LongRunning, - TaskScheduler.Current); + await Task.Factory.StartNew(DoHandleClientAsync, args, serverShutdownCancellationToken, + TaskCreationOptions.LongRunning, TaskScheduler.Default); } OnServerStopped(); diff --git a/NetSharp/NetSharp/Servers/UdpServer.cs b/NetSharp/NetSharp/Servers/UdpServer.cs @@ -2,6 +2,7 @@ using System.Collections.Concurrent; using System.Net; using System.Net.Sockets; +using System.Threading; using System.Threading.Channels; using System.Threading.Tasks; using NetSharp.Interfaces; @@ -18,27 +19,21 @@ namespace NetSharp.Servers public sealed class UdpServer : Server { /// <summary> - /// Holds currently connected and active clients, as well as their current received packet queues. - /// </summary> - private readonly ConcurrentDictionary<EndPoint, Channel<Packet>> activeClients; - - /// <summary> /// The options that should be applied to every channel created to handle a client. /// </summary> private static readonly UnboundedChannelOptions clientChannelOptions = new UnboundedChannelOptions - { - SingleReader = true, - SingleWriter = true - }; - - /// <inheritdoc /> - public UdpServer() : base(SocketType.Dgram, ProtocolType.Udp, SocketOptionManager.Udp) { - activeClients = new ConcurrentDictionary<EndPoint, Channel<Packet>>(); - } + SingleReader = true, + SingleWriter = true + }; + + /// <summary> + /// Holds currently connected and active clients, as well as their current received packet queues. + /// </summary> + private readonly ConcurrentDictionary<EndPoint, Channel<Packet>> activeClients; /// <inheritdoc /> - protected override async Task HandleClientAsync(ClientHandlerArgs args) + protected override async Task HandleClientAsync(ClientHandlerArgs args, CancellationToken cancellationToken) { EndPoint clientEndPoint = args.ClientEndPoint; Channel<Packet> clientPacketBuffer = activeClients[clientEndPoint]; @@ -50,11 +45,13 @@ namespace NetSharp.Servers do { // receive a single raw packet from the network - Packet rawRequest = await clientPacketBuffer.Reader.ReadAsync(serverShutdownCancellationTokenSource.Token); + Packet rawRequest = await clientPacketBuffer.Reader.ReadAsync(cancellationToken); - if (rawRequest.Equals(NullPacket) || rawRequest.Type == PacketRegistry.GetPacketId<DisconnectPacket>()) + if (rawRequest.Equals(NullPacket) || + rawRequest.Type == PacketRegistry.GetPacketId<DisconnectPacket>()) { - logger.LogMessage($"Received a disconnect packet from client socket: [Remote EP: {clientEndPoint}]"); + logger.LogMessage( + $"Received a disconnect packet from client socket: [Remote EP: {clientEndPoint}]"); break; } @@ -75,45 +72,57 @@ namespace NetSharp.Servers if (responsePacketType == null) { - logger.LogError($"Response packet type for request packet of type {requestPacketType} is null"); + logger.LogError( + $"Response packet type for request packet of type {requestPacketType} is null"); continue; } uint responsePacketTypeId = PacketRegistry.GetPacketId(responsePacketType); responsePacket.BeforeSerialisation(); - Packet rawResponse = new Packet(responsePacket.Serialise(), responsePacketTypeId, NetworkErrorCode.Ok); + Packet rawResponse = new Packet(responsePacket.Serialise(), responsePacketTypeId, + NetworkErrorCode.Ok); // echo back the processed raw response to the network - bool sentCorrectly = - await DoSendPacketToAsync(socket, clientEndPoint, rawResponse, SocketFlags.None, - DefaultNetworkOperationTimeout); + bool sentCorrectly = await DoSendPacketToAsync(socket, clientEndPoint, rawResponse, SocketFlags.None, + NetworkOperationTimeout, cancellationToken); if (!sentCorrectly) { - logger.LogWarning($"Could not send response back to client socket: [Remote EP: {clientEndPoint}]"); + logger.LogWarning( + $"Could not send response back to client socket: [Remote EP: {clientEndPoint}]"); break; } } while (true); - + } + finally + { logger.LogMessage($"Stopping client handler for client socket: [Remote EP: {clientEndPoint}]"); if (activeClients.TryRemove(clientEndPoint, out Channel<Packet> packetChannel)) { packetChannel.Writer.Complete(); - logger.LogMessage($"Shutting down packet channel for client socket: [Remote EP: {clientEndPoint}]"); + logger.LogMessage( + $"Shutting down packet channel for client socket: [Remote EP: {clientEndPoint}]"); } else { - logger.LogMessage($"Couldn't shut down packet channel for client socket: [Remote EP: {clientEndPoint}]"); + logger.LogMessage( + $"Couldn't shut down packet channel for client socket: [Remote EP: {clientEndPoint}]"); } } - catch (TaskCanceledException) { logger.LogMessage("Client handling was cancelled via a task cancellation."); } - catch (OperationCanceledException) { logger.LogMessage("Client handling was cancelled via an operation cancellation."); } - catch (Exception ex) - { - logger.LogException("Exception during client socket handling:", ex); - } + } + + /// <inheritdoc /> + public UdpServer(TimeSpan networkOperationTimeout) : base(SocketType.Dgram, ProtocolType.Udp, SocketOptionManager.Udp, + networkOperationTimeout) + { + activeClients = new ConcurrentDictionary<EndPoint, Channel<Packet>>(); + } + + /// <inheritdoc /> + public UdpServer() : this(DefaultNetworkOperationTimeout) + { } /// <inheritdoc /> @@ -134,24 +143,23 @@ namespace NetSharp.Servers { EndPoint nullEndPoint = new IPEndPoint(IPAddress.Any, 0); (Packet request, TransmissionResult packetResult) = - await DoReceivePacketFromAsync(socket, nullEndPoint, SocketFlags.None, DefaultNetworkOperationTimeout); + await DoReceivePacketFromAsync(socket, nullEndPoint, SocketFlags.None, Timeout.InfiniteTimeSpan, + serverShutdownCancellationToken); EndPoint clientEndPoint = packetResult.RemoteEndPoint; if (request.Equals(NullPacket) || packetResult.Equals(NullTransmissionResult)) { continue; } - + if (!activeClients.ContainsKey(clientEndPoint)) { ClientHandlerArgs args = ClientHandlerArgs.ForUdpClientHandler(in clientEndPoint); activeClients.TryAdd(clientEndPoint, Channel.CreateUnbounded<Packet>(clientChannelOptions)); - await Task.Factory.StartNew(DoHandleClientAsync, args, - serverShutdownCancellationTokenSource.Token, - TaskCreationOptions.LongRunning, - TaskScheduler.Current); + await Task.Factory.StartNew(DoHandleClientAsync, args, serverShutdownCancellationToken, + TaskCreationOptions.LongRunning, TaskScheduler.Default); } await activeClients[clientEndPoint].Writer.WriteAsync(request); diff --git a/NetSharp/NetSharp/Utils/Constants.cs b/NetSharp/NetSharp/Utils/Constants.cs @@ -9,10 +9,5 @@ /// The default port over which a connection is made. /// </summary> internal const int DefaultPort = 12374; - - /// <summary> - /// The largest byte buffer that can be sent via UDP. - /// </summary> - internal const int UdpMaxBufferSize = 60_000; } } \ No newline at end of file diff --git a/NetSharp/NetSharp/Utils/Conversion/EndianAwareBitConverter.cs b/NetSharp/NetSharp/Utils/Conversion/EndianAwareBitConverter.cs @@ -23,120 +23,140 @@ namespace NetSharp.Utils.Conversion } /// <inheritdoc cref="BitConverter.GetBytes(bool)"/> + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Span<byte> GetBytes(bool value, bool littleEndian = false) { return ReverseAsNeeded(BitConverter.GetBytes(value), littleEndian); } /// <inheritdoc cref="BitConverter.GetBytes(char)"/> + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Span<byte> GetBytes(char value, bool littleEndian = false) { return ReverseAsNeeded(BitConverter.GetBytes(value), littleEndian); } /// <inheritdoc cref="BitConverter.GetBytes(double)"/> + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Span<byte> GetBytes(double value, bool littleEndian = false) { return ReverseAsNeeded(BitConverter.GetBytes(value), littleEndian); } /// <inheritdoc cref="BitConverter.GetBytes(float)"/> + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Span<byte> GetBytes(float value, bool littleEndian = false) { return ReverseAsNeeded(BitConverter.GetBytes(value), littleEndian); } /// <inheritdoc cref="BitConverter.GetBytes(int)"/> + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Span<byte> GetBytes(int value, bool littleEndian = false) { return ReverseAsNeeded(BitConverter.GetBytes(value), littleEndian); } /// <inheritdoc cref="BitConverter.GetBytes(long)"/> + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Span<byte> GetBytes(long value, bool littleEndian = false) { return ReverseAsNeeded(BitConverter.GetBytes(value), littleEndian); } /// <inheritdoc cref="BitConverter.GetBytes(short)"/> + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Span<byte> GetBytes(short value, bool littleEndian = false) { return ReverseAsNeeded(BitConverter.GetBytes(value), littleEndian); } /// <inheritdoc cref="BitConverter.GetBytes(uint)"/> + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Span<byte> GetBytes(uint value, bool littleEndian = false) { return ReverseAsNeeded(BitConverter.GetBytes(value), littleEndian); } /// <inheritdoc cref="BitConverter.GetBytes(ulong)"/> + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Span<byte> GetBytes(ulong value, bool littleEndian = false) { return ReverseAsNeeded(BitConverter.GetBytes(value), littleEndian); } /// <inheritdoc cref="BitConverter.GetBytes(ushort)"/> + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Span<byte> GetBytes(ushort value, bool littleEndian = false) { return ReverseAsNeeded(BitConverter.GetBytes(value), littleEndian); } /// <inheritdoc cref="BitConverter.ToBoolean(ReadOnlySpan{byte})"/> + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool ToBoolean(Span<byte> bytes, bool littleEndian = false) { return BitConverter.ToBoolean(ReverseAsNeeded(bytes, littleEndian)); } /// <inheritdoc cref="BitConverter.ToChar(ReadOnlySpan{byte})"/> + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static char ToChar(Span<byte> bytes, bool littleEndian = false) { return BitConverter.ToChar(ReverseAsNeeded(bytes, littleEndian)); } /// <inheritdoc cref="BitConverter.ToDouble(ReadOnlySpan{byte})"/> + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double ToDouble(Span<byte> bytes, bool littleEndian = false) { return BitConverter.ToDouble(ReverseAsNeeded(bytes, littleEndian)); } /// <inheritdoc cref="BitConverter.ToInt16(ReadOnlySpan{byte})"/> + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static short ToInt16(Span<byte> bytes, bool littleEndian = false) { return BitConverter.ToInt16(ReverseAsNeeded(bytes, littleEndian)); } /// <inheritdoc cref="BitConverter.ToInt32(ReadOnlySpan{byte})"/> + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int ToInt32(Span<byte> bytes, bool littleEndian = false) { return BitConverter.ToInt32(ReverseAsNeeded(bytes, littleEndian)); } /// <inheritdoc cref="BitConverter.ToInt64(ReadOnlySpan{byte})"/> + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static long ToInt64(Span<byte> bytes, bool littleEndian = false) { return BitConverter.ToInt64(ReverseAsNeeded(bytes, littleEndian)); } /// <inheritdoc cref="BitConverter.ToSingle(ReadOnlySpan{byte})"/> + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float ToSingle(Span<byte> bytes, bool littleEndian = false) { return BitConverter.ToSingle(ReverseAsNeeded(bytes, littleEndian)); } /// <inheritdoc cref="BitConverter.ToUInt16(ReadOnlySpan{byte})"/> + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ushort ToUInt16(byte[] bytes, bool littleEndian = false) { return BitConverter.ToUInt16(ReverseAsNeeded(bytes, littleEndian)); } /// <inheritdoc cref="BitConverter.ToUInt32(ReadOnlySpan{byte})"/> + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint ToUInt32(Span<byte> bytes, bool littleEndian = false) { return BitConverter.ToUInt32(ReverseAsNeeded(bytes, littleEndian)); } /// <inheritdoc cref="BitConverter.ToUInt64(ReadOnlySpan{byte})"/> + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ulong ToUInt64(Span<byte> bytes, bool littleEndian = false) { return BitConverter.ToUInt64(ReverseAsNeeded(bytes, littleEndian)); diff --git a/NetSharp/NetSharp/Utils/NetworkOperations.cs b/NetSharp/NetSharp/Utils/NetworkOperations.cs @@ -3,7 +3,6 @@ using System.Net; using System.Net.Sockets; using System.Runtime.CompilerServices; using System.Threading; -using System.Threading.Tasks; using NetSharp.Packets; using NetSharp.Utils.Conversion; @@ -12,84 +11,109 @@ namespace NetSharp.Utils /// <summary> /// Helper class for asynchronously performing common network operations, for both the UDP and TCP protocols. /// </summary> - public static class NetworkOperations + internal static class NetworkOperations { /// <summary> - /// Reads the specified amount of data asynchronously from the network, via the given socket. - /// The given <see cref="SocketFlags"/> are associated with the read, and the given <see cref="CancellationToken"/> - /// is used to allow for asynchronous task cancellation. + /// Reads the specified amount of data from the network, via the given socket. + /// The given <see cref="SocketFlags"/> are associated with the read. /// </summary> /// <param name="socket">The socket which should read data from the network.</param> /// <param name="count">The number of bytes to read from the network.</param> /// <param name="socketFlags">The socket flags associated with the receive operation.</param> - /// <param name="cancellationToken">The cancellation token to use for asynchronous cancellation.</param> /// <returns>The result of the receive operation.</returns> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static Task<TransmissionResult> ReadAsync(Socket socket, int count, SocketFlags socketFlags, - CancellationToken cancellationToken) + private static TransmissionResult Read(Socket socket, int count, SocketFlags socketFlags) { - return Task.Factory.StartNew(() => - { - byte[] byteBuffer = new byte[count]; - int receivedBytesCount = 0; + byte[] byteBuffer = new byte[count]; + int receivedBytesCount = 0; - while (count > receivedBytesCount) - { - Span<byte> receivedBytes = new Span<byte>(byteBuffer, receivedBytesCount, count - receivedBytesCount); + while (count > receivedBytesCount) + { + Span<byte> receivedBytes = new Span<byte>(byteBuffer, receivedBytesCount, count - receivedBytesCount); - receivedBytesCount += socket.Receive(receivedBytes, socketFlags); - } + receivedBytesCount += socket.Receive(receivedBytes, socketFlags); + } - return new TransmissionResult(byteBuffer, receivedBytesCount, socket.RemoteEndPoint); - }, cancellationToken); + return new TransmissionResult(byteBuffer, receivedBytesCount, socket.RemoteEndPoint); ; } /// <summary> - /// Reads a datagram asynchronously from the given remote endpoint, via the given socket. - /// The given <see cref="SocketFlags"/> are associated with the read, and the given <see cref="CancellationToken"/> - /// is used to allow for asynchronous task cancellation. + /// Reads a datagram segment of the given length from the given remote endpoint, via the given socket. + /// The given <see cref="SocketFlags"/> are associated with the read. /// </summary> /// <param name="socket">The socket which should read data from the network.</param> /// <param name="count">The number of bytes to read from the network.</param> /// <param name="remoteEndPoint">The remote endpoint from which data should be read.</param> /// <param name="socketFlags">The socket flags associated with the receive operation.</param> - /// <param name="cancellationToken">The cancellation token to use for asynchronous cancellation.</param> /// <returns>The result of the receive operation.</returns> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static Task<TransmissionResult> ReadFromAsync(Socket socket, int count, EndPoint remoteEndPoint, - SocketFlags socketFlags, CancellationToken cancellationToken) + private static TransmissionResult ReadFrom(Socket socket, int count, EndPoint remoteEndPoint, SocketFlags socketFlags) { - return Task.Factory.StartNew(() => + byte[] byteBuffer = new byte[count]; + EndPoint actualRemoteEndPoint = remoteEndPoint; + int receivedBytesCount = 0; + + while (count > receivedBytesCount) { - byte[] byteBuffer = new byte[count]; - EndPoint actualRemoteEndPoint = remoteEndPoint; - int receivedBytesCount = 0; + receivedBytesCount += + socket.ReceiveMessageFrom(byteBuffer, receivedBytesCount, count - receivedBytesCount, + ref socketFlags, ref actualRemoteEndPoint, out IPPacketInformation _); + } + + return new TransmissionResult(byteBuffer, receivedBytesCount, actualRemoteEndPoint); + } - while (count > receivedBytesCount) - { - IPPacketInformation _; + /// <summary> + /// Writes the given data buffer to the network, via the given socket. + /// The given <see cref="SocketFlags"/> are associated with the write, and the given <see cref="CancellationToken"/> + /// is used to allow for asynchronous task cancellation. + /// </summary> + /// <param name="socket">The socket which should write data to the network.</param> + /// <param name="buffer">The buffer that should be written to the network.</param> + /// <param name="socketFlags">The socket flags associated with the send operation.</param> + private static void Write(Socket socket, ReadOnlyMemory<byte> buffer, SocketFlags socketFlags) + { + int bytesToSend = buffer.Length; + int sentBytesCount = 0; - receivedBytesCount += socket.ReceiveMessageFrom(byteBuffer, receivedBytesCount, - count - receivedBytesCount, ref socketFlags, ref actualRemoteEndPoint, out _); - } + while (bytesToSend > sentBytesCount) + { + ReadOnlySpan<byte> bufferSegment = buffer.Span.Slice(sentBytesCount, bytesToSend - sentBytesCount); - return new TransmissionResult(byteBuffer, receivedBytesCount, actualRemoteEndPoint); - }, cancellationToken); + sentBytesCount += socket.Send(bufferSegment, socketFlags); + } } /// <summary> - /// Reads a packet asynchronously from network, via the given socket. The given <see cref="SocketFlags"/> are - /// associated with the read, and the given <see cref="CancellationToken"/> is used to allow for asynchronous - /// task cancellation. + /// Writes the given data buffer to the given remote endpoint, via the given socket. + /// The given <see cref="SocketFlags"/> are associated with the write, and the given <see cref="CancellationToken"/> + /// is used to allow for asynchronous task cancellation. + /// </summary> + /// <param name="socket">The socket which should write data to the network.</param> + /// <param name="remoteEndPoint">The remote endpoint to which data should be written.</param> + /// <param name="buffer">The buffer that should be written to the network.</param> + /// <param name="socketFlags">The socket flags associated with the send operation.</param> + private static void WriteTo(Socket socket, EndPoint remoteEndPoint, ReadOnlyMemory<byte> buffer, SocketFlags socketFlags) + { + int bytesToSend = buffer.Length; + int sentBytesCount = 0; + + while (bytesToSend > sentBytesCount) + { + ReadOnlySpan<byte> bufferSegment = buffer.Span.Slice(sentBytesCount, bytesToSend - sentBytesCount); + + sentBytesCount += socket.SendTo(bufferSegment.ToArray(), socketFlags, remoteEndPoint); + } + } + + /// <summary> + /// Reads a packet from network, via the given socket. + /// The given <see cref="SocketFlags"/> are associated with the read. /// </summary> /// <param name="socket">The socket which should read the packet from the network.</param> /// <param name="socketFlags">The socket flags associated with the receive operation.</param> - /// <param name="cancellationToken">The cancellation token to use for asynchronous cancellation.</param> /// <returns>The read packet.</returns> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static async Task<Packet> ReadPacketAsync(Socket socket, SocketFlags socketFlags, CancellationToken cancellationToken) + internal static Packet ReadPacket(Socket socket, SocketFlags socketFlags) { - TransmissionResult packetHeaderResult = await ReadAsync(socket, Packet.HeaderSize, socketFlags, cancellationToken); + TransmissionResult packetHeaderResult = Read(socket, Packet.HeaderSize, socketFlags); int packetSize = EndianAwareBitConverter.ToInt32(packetHeaderResult.Buffer.Span.Slice(0, sizeof(int))); @@ -98,7 +122,7 @@ namespace NetSharp.Utils return Packet.Deserialise(packetHeaderResult.Buffer); } - TransmissionResult packetDataResult = await ReadAsync(socket, packetSize, socketFlags, cancellationToken); + TransmissionResult packetDataResult = Read(socket, packetSize, socketFlags); byte[] serialisedPacket = new byte[Packet.HeaderSize + packetSize]; @@ -112,21 +136,18 @@ namespace NetSharp.Utils } /// <summary> - /// Reads a packet asynchronously from the given remote endpoint, via the given socket. The given - /// <see cref="SocketFlags"/> are associated with the read, and the given <see cref="CancellationToken"/> is - /// used to allow for asynchronous task cancellation. + /// Reads a packet from the given remote endpoint, via the given socket. + /// The given <see cref="SocketFlags"/> are associated with the read. /// </summary> /// <param name="socket">The socket which should read the packet from the network.</param> /// <param name="remoteEndPoint">The remote endpoint from which a packet should be read.</param> /// <param name="socketFlags">The socket flags associated with the receive operation.</param> - /// <param name="cancellationToken">The cancellation token to use for asynchronous cancellation.</param> /// <returns>The read packet and associated transmission results.</returns> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static async Task<(Packet packet, TransmissionResult packetResult)> ReadPacketFromAsync( - Socket socket, EndPoint remoteEndPoint, SocketFlags socketFlags, CancellationToken cancellationToken) + internal static (Packet packet, TransmissionResult packetResult) ReadPacketFrom( + Socket socket, EndPoint remoteEndPoint, SocketFlags socketFlags) { TransmissionResult packetHeaderResult = - await ReadFromAsync(socket, Packet.HeaderSize, remoteEndPoint, socketFlags, cancellationToken); + ReadFrom(socket, Packet.HeaderSize, remoteEndPoint, socketFlags); int packetSize = EndianAwareBitConverter.ToInt32(packetHeaderResult.Buffer.Span.Slice(0, sizeof(int))); @@ -136,7 +157,7 @@ namespace NetSharp.Utils } TransmissionResult packetDataResult = - await ReadFromAsync(socket, packetSize, packetHeaderResult.RemoteEndPoint, socketFlags, cancellationToken); + ReadFrom(socket, packetSize, packetHeaderResult.RemoteEndPoint, socketFlags); byte[] serialisedPacket = new byte[Packet.HeaderSize + packetSize]; @@ -150,87 +171,24 @@ namespace NetSharp.Utils } /// <summary> - /// Writes the given buffer asynchronously to the network, via the given socket. - /// The given <see cref="SocketFlags"/> are associated with the write, and the given <see cref="CancellationToken"/> - /// is used to allow for asynchronous task cancellation. - /// </summary> - /// <param name="socket">The socket which should write data to the network.</param> - /// <param name="buffer">The buffer that should be written to the network.</param> - /// <param name="socketFlags">The socket flags associated with the send operation.</param> - /// <param name="cancellationToken">The cancellation token to use for asynchronous cancellation.</param> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static Task WriteAsync(Socket socket, ReadOnlyMemory<byte> buffer, SocketFlags socketFlags, - CancellationToken cancellationToken) - { - return Task.Factory.StartNew(() => - { - int bytesToSend = buffer.Length; - int sentBytesCount = 0; - - while (bytesToSend > sentBytesCount) - { - ReadOnlySpan<byte> bufferSegment = buffer.Span.Slice(sentBytesCount, bytesToSend - sentBytesCount); - - sentBytesCount += socket.Send(bufferSegment, socketFlags); - } - }, cancellationToken); - } - - /// <summary> - /// Writes the given packet asynchronously to the network, via the given socket. The given <see cref="SocketFlags"/> - /// are associated with the write, and the given <see cref="CancellationToken"/> is used to allow for asynchronous - /// task cancellation. + /// Writes the given packet to the network, via the given socket. + /// The given <see cref="SocketFlags"/> are associated with the write. /// </summary> /// <param name="socket">The socket which should write data to the network.</param> /// <param name="packet">The packet that should be written to the network.</param> /// <param name="socketFlags">The socket flags associated with the send operation.</param> - /// <param name="cancellationToken">The cancellation token to use for asynchronous cancellation.</param> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Task WritePacketAsync(Socket socket, Packet packet, SocketFlags socketFlags, - CancellationToken cancellationToken) - => WriteAsync(socket, Packet.Serialise(packet), socketFlags, cancellationToken); + internal static void WritePacket(Socket socket, Packet packet, SocketFlags socketFlags) + => Write(socket, Packet.Serialise(packet), socketFlags); /// <summary> - /// Writes the given packet asynchronously to the given remote endpoint, via the given socket. - /// The given <see cref="SocketFlags"/> are associated with the write, and the given <see cref="CancellationToken"/> - /// is used to allow for asynchronous task cancellation. + /// Writes the given packet to the given remote endpoint, via the given socket. + /// The given <see cref="SocketFlags"/> are associated with the write. /// </summary> /// <param name="socket">The socket which should write data to the network.</param> /// <param name="remoteEndPoint">The remote endpoint to which data should be written.</param> /// <param name="packet">The packet that should be written to the remote endpoint.</param> /// <param name="socketFlags">The socket flags associated with the send operation.</param> - /// <param name="cancellationToken">The cancellation token to use for asynchronous cancellation.</param> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Task WritePacketToAsync(Socket socket, EndPoint remoteEndPoint, Packet packet, - SocketFlags socketFlags, CancellationToken cancellationToken) - => WriteToAsync(socket, remoteEndPoint, Packet.Serialise(packet), socketFlags, cancellationToken); - - /// <summary> - /// Writes the given buffer asynchronously to the given remote endpoint, via the given socket. - /// The given <see cref="SocketFlags"/> are associated with the write, and the given <see cref="CancellationToken"/> - /// is used to allow for asynchronous task cancellation. - /// </summary> - /// <param name="socket">The socket which should write data to the network.</param> - /// <param name="remoteEndPoint">The remote endpoint to which data should be written.</param> - /// <param name="buffer">The buffer that should be written to the network.</param> - /// <param name="socketFlags">The socket flags associated with the send operation.</param> - /// <param name="cancellationToken">The cancellation token to use for asynchronous cancellation.</param> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static Task WriteToAsync(Socket socket, EndPoint remoteEndPoint, ReadOnlyMemory<byte> buffer, - SocketFlags socketFlags, CancellationToken cancellationToken) - { - return Task.Factory.StartNew(() => - { - int bytesToSend = buffer.Length; - int sentBytesCount = 0; - - while (bytesToSend > sentBytesCount) - { - ReadOnlySpan<byte> bufferSegment = buffer.Span.Slice(sentBytesCount, bytesToSend - sentBytesCount); - - sentBytesCount += socket.SendTo(bufferSegment.ToArray(), socketFlags, remoteEndPoint); - } - }, cancellationToken); - } + internal static void WritePacketTo(Socket socket, EndPoint remoteEndPoint, Packet packet, SocketFlags socketFlags) + => WriteTo(socket, remoteEndPoint, Packet.Serialise(packet), socketFlags); } } \ No newline at end of file diff --git a/NetSharp/NetSharpExamples/Program.cs b/NetSharp/NetSharpExamples/Program.cs @@ -15,12 +15,11 @@ namespace NetSharpExamples { internal class Program { + private static int newtorkTimeout = 10; private static IPAddress serverAddress; private static int serverPort; - private static int newtorkTimeout = 1_000_000; - private static async Task Main() { Console.WriteLine("Hello World!"); @@ -61,7 +60,7 @@ namespace NetSharpExamples { TimeSpan socketTimeout = TimeSpan.FromSeconds(newtorkTimeout); - const int clientCount = 1; + const int clientCount = 10; const int sentPacketCount = 1_000_000; static Client ClientFactory() @@ -134,7 +133,7 @@ namespace NetSharpExamples { Console.WriteLine("Socket could not be bound"); } - }, TaskCreationOptions.LongRunning).Result; + }, TaskCreationOptions.LongRunning); } Console.ReadLine();