commit c28d5fabdf15a8f4310c5fd6e8fa443ab574f1a5
parent 219a7a72da500fac5a58a504e61db458fc95610a
Author: Mikolaj Lenczewski <mikolaj.lenczewski308@gmail.com>
Date: Sun, 12 Apr 2020 14:18:37 +0100
Added some light docs and cancellation support for the SocketClient.ConnectAsync method
Diffstat:
8 files changed, 244 insertions(+), 91 deletions(-)
diff --git a/NetSharp/NetSharp/Sockets/Datagram/DatagramSocketClient.cs b/NetSharp/NetSharp/Sockets/Datagram/DatagramSocketClient.cs
@@ -1,4 +1,5 @@
-using NetSharp.Utils;
+using NetSharp.Packets;
+using NetSharp.Utils;
using System;
using System.Net;
@@ -8,16 +9,31 @@ using System.Threading.Tasks;
namespace NetSharp.Sockets.Datagram
{
- //TODO fix memory leak issue
+ //TODO document
+ public readonly struct DatagramSocketClientOptions
+ {
+ public static readonly DatagramSocketClientOptions Defaults =
+ new DatagramSocketClientOptions(NetworkPacket.TotalSize);
+
+ public readonly int PacketSize;
+
+ public DatagramSocketClientOptions(int packetSize)
+ {
+ PacketSize = packetSize;
+ }
+ }
+
//TODO address the need to handle series of network packets, not just single packets
//TODO document class
public sealed class DatagramSocketClient : SocketClient
{
- public DatagramSocketClient(in AddressFamily connectionAddressFamily, in ProtocolType connectionProtocolType)
- : base(in connectionAddressFamily, SocketType.Dgram, in connectionProtocolType)
+ public DatagramSocketClient(in AddressFamily connectionAddressFamily, in ProtocolType connectionProtocolType,
+ in DatagramSocketClientOptions? clientOptions = null) : base(in connectionAddressFamily, SocketType.Dgram,
+ in connectionProtocolType, clientOptions?.PacketSize ?? DatagramSocketClientOptions.Defaults.PacketSize)
{
}
+ /// <inheritdoc />
protected override SocketAsyncEventArgs CreateTransmissionArgs()
{
SocketAsyncEventArgs connectionArgs = new SocketAsyncEventArgs();
@@ -27,15 +43,18 @@ namespace NetSharp.Sockets.Datagram
return connectionArgs;
}
+ /// <inheritdoc />
protected override void ResetTransmissionArgs(SocketAsyncEventArgs args)
{
}
+ /// <inheritdoc />
protected override bool CanTransmissionArgsBeReused(in SocketAsyncEventArgs args)
{
return true;
}
+ /// <inheritdoc />
protected override void DestroyTransmissionArgs(SocketAsyncEventArgs remoteConnectionArgs)
{
remoteConnectionArgs.Completed -= HandleIoCompleted;
@@ -43,6 +62,7 @@ namespace NetSharp.Sockets.Datagram
remoteConnectionArgs.Dispose();
}
+ /// <inheritdoc />
protected override void HandleIoCompleted(object sender, SocketAsyncEventArgs args)
{
switch (args.LastOperation)
@@ -118,7 +138,7 @@ namespace NetSharp.Sockets.Datagram
public TransmissionResult ReceiveFrom(ref EndPoint remoteEndPoint, byte[] receiveBuffer, SocketFlags flags = SocketFlags.None)
{
- int receivedBytes = connection.ReceiveFrom(receiveBuffer, flags, ref remoteEndPoint);
+ int receivedBytes = Connection.ReceiveFrom(receiveBuffer, flags, ref remoteEndPoint);
return new TransmissionResult(in receiveBuffer, in receivedBytes, in remoteEndPoint);
}
@@ -136,7 +156,7 @@ namespace NetSharp.Sockets.Datagram
args.SocketFlags = flags;
args.UserToken = new AsyncTransmissionToken(in tcs, in cancellationToken);
- if (connection.ReceiveFromAsync(args)) return new ValueTask<TransmissionResult>(tcs.Task);
+ if (Connection.ReceiveFromAsync(args)) return new ValueTask<TransmissionResult>(tcs.Task);
TransmissionResult result = new TransmissionResult(in args);
@@ -147,7 +167,7 @@ namespace NetSharp.Sockets.Datagram
public TransmissionResult SendTo(EndPoint remoteEndPoint, byte[] sendBuffer, SocketFlags flags = SocketFlags.None)
{
- int sentBytes = connection.SendTo(sendBuffer, flags, remoteEndPoint);
+ int sentBytes = Connection.SendTo(sendBuffer, flags, remoteEndPoint);
return new TransmissionResult(in sendBuffer, in sentBytes, in remoteEndPoint);
}
@@ -165,7 +185,7 @@ namespace NetSharp.Sockets.Datagram
args.SocketFlags = flags;
args.UserToken = new AsyncTransmissionToken(in tcs, in cancellationToken);
- if (connection.SendToAsync(args)) return new ValueTask<TransmissionResult>(tcs.Task);
+ if (Connection.SendToAsync(args)) return new ValueTask<TransmissionResult>(tcs.Task);
TransmissionResult result = new TransmissionResult(in args);
diff --git a/NetSharp/NetSharp/Sockets/Datagram/DatagramSocketServer.cs b/NetSharp/NetSharp/Sockets/Datagram/DatagramSocketServer.cs
@@ -1,4 +1,5 @@
-using NetSharp.Utils;
+using NetSharp.Packets;
+using NetSharp.Utils;
using System;
using System.Net;
@@ -6,15 +7,13 @@ using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
-using NetworkPacket = NetSharp.Packets.NetworkPacket;
-
namespace NetSharp.Sockets.Datagram
{
//TODO document
public readonly struct DatagramSocketServerOptions
{
public static readonly DatagramSocketServerOptions Defaults =
- new DatagramSocketServerOptions(NetworkPacket.TotalSize, 8);
+ new DatagramSocketServerOptions(NetworkPacket.TotalSize, Environment.ProcessorCount);
public readonly int PacketSize;
@@ -28,7 +27,6 @@ namespace NetSharp.Sockets.Datagram
}
}
- //TODO fix memory leak issue
//TODO address the need to handle series of network packets, not just single packets
//TODO allow for the server to do more than just echo packets
//TODO document class
@@ -36,13 +34,13 @@ namespace NetSharp.Sockets.Datagram
{
private static readonly EndPoint AnyRemoteEndPoint = new IPEndPoint(IPAddress.Any, 0);
- public readonly DatagramSocketServerOptions ServerOptions;
+ private readonly DatagramSocketServerOptions serverOptions;
public DatagramSocketServer(in AddressFamily connectionAddressFamily, in ProtocolType connectionProtocolType,
in DatagramSocketServerOptions? serverOptions = null) : base(in connectionAddressFamily, SocketType.Dgram,
- in connectionProtocolType)
+ in connectionProtocolType, serverOptions?.PacketSize ?? DatagramSocketServerOptions.Defaults.PacketSize)
{
- ServerOptions = serverOptions ?? DatagramSocketServerOptions.Defaults;
+ this.serverOptions = serverOptions ?? DatagramSocketServerOptions.Defaults;
}
private readonly struct SocketOperationToken
@@ -55,6 +53,7 @@ namespace NetSharp.Sockets.Datagram
}
}
+ /// <inheritdoc />
protected override SocketAsyncEventArgs CreateTransmissionArgs()
{
SocketAsyncEventArgs connectionArgs = new SocketAsyncEventArgs();
@@ -64,15 +63,18 @@ namespace NetSharp.Sockets.Datagram
return connectionArgs;
}
+ /// <inheritdoc />
protected override void ResetTransmissionArgs(SocketAsyncEventArgs args)
{
}
+ /// <inheritdoc />
protected override bool CanTransmissionArgsBeReused(in SocketAsyncEventArgs args)
{
return true;
}
+ /// <inheritdoc />
protected override void DestroyTransmissionArgs(SocketAsyncEventArgs remoteConnectionArgs)
{
remoteConnectionArgs.Completed -= HandleIoCompleted;
@@ -80,6 +82,7 @@ namespace NetSharp.Sockets.Datagram
remoteConnectionArgs.Dispose();
}
+ /// <inheritdoc />
protected override void HandleIoCompleted(object sender, SocketAsyncEventArgs args)
{
switch (args.LastOperation)
@@ -110,17 +113,16 @@ namespace NetSharp.Sockets.Datagram
receiveArgs.SetBuffer(receiveBufferMemory);
receiveArgs.UserToken = new SocketOperationToken(in receiveBuffer);
- bool operationPending = connection.ReceiveFromAsync(receiveArgs);
+ bool operationPending = Connection.ReceiveFromAsync(receiveArgs);
- if (!operationPending)
- {
- SocketAsyncEventArgs newReceiveArgs = TransmissionArgsPool.Rent();
- newReceiveArgs.RemoteEndPoint = AnyRemoteEndPoint;
+ if (operationPending) return;
- ReceiveFrom(newReceiveArgs); // start a new receive from operation immediately, to not drop any packets
+ SocketAsyncEventArgs newReceiveArgs = TransmissionArgsPool.Rent();
+ newReceiveArgs.RemoteEndPoint = AnyRemoteEndPoint;
- CompleteReceiveFrom(receiveArgs);
- }
+ ReceiveFrom(newReceiveArgs); // start a new receive from operation immediately, to not drop any packets
+
+ CompleteReceiveFrom(receiveArgs);
}
private void CompleteReceiveFrom(SocketAsyncEventArgs receiveArgs)
@@ -157,7 +159,7 @@ namespace NetSharp.Sockets.Datagram
private void SendTo(SocketAsyncEventArgs sendArgs)
{
- bool operationPending = connection.SendToAsync(sendArgs);
+ bool operationPending = Connection.SendToAsync(sendArgs);
if (!operationPending)
{
@@ -184,6 +186,12 @@ namespace NetSharp.Sockets.Datagram
TransmissionArgsPool.Return(sendArgs);
}
+ public ref readonly DatagramSocketServerOptions ServerOptions
+ {
+ get { return ref serverOptions; }
+ }
+
+ /// <inheritdoc />
public override Task RunAsync(CancellationToken cancellationToken = default)
{
for (int i = 0; i < ServerOptions.ConcurrentReceiveFromCalls; i++)
diff --git a/NetSharp/NetSharp/Sockets/SocketClient.cs b/NetSharp/NetSharp/Sockets/SocketClient.cs
@@ -7,7 +7,9 @@ using System.Threading.Tasks;
namespace NetSharp.Sockets
{
- //TODO document class
+ /// <summary>
+ /// Abstract base class for clients.
+ /// </summary>
public abstract class SocketClient : SocketConnection
{
//TODO document
@@ -55,16 +57,37 @@ namespace NetSharp.Sockets
}
}
- protected SocketClient(in AddressFamily connectionAddressFamily, in SocketType connectionSocketType, in ProtocolType connectionProtocolType)
- : base(in connectionAddressFamily, in connectionSocketType, in connectionProtocolType)
+ /// <summary>
+ /// Constructs a new instance of the <see cref="SocketClient"/> class.
+ /// </summary>
+ /// <inheritdoc />
+ protected SocketClient(in AddressFamily connectionAddressFamily, in SocketType connectionSocketType,
+ in ProtocolType connectionProtocolType, in int maxPooledBufferLength) : base(in connectionAddressFamily,
+ in connectionSocketType, in connectionProtocolType, in maxPooledBufferLength)
{
}
+ /// <summary>
+ /// Connects the client to the specified end point. If called on a <see cref="SocketType.Dgram"/>-based client,
+ /// this method configures the default remote host, and the client will ignore any packets not coming from this
+ /// default host (i.e the given <paramref name="remoteEndPoint"/>).
+ /// </summary>
+ /// <param name="remoteEndPoint">The remote end point which to which to connect the client.</param>
public void Connect(in EndPoint remoteEndPoint)
{
- connection.Connect(remoteEndPoint);
+ Connection.Connect(remoteEndPoint);
}
+ /// <summary>
+ /// Asynchronously connects the client to the specified end point. If called on a
+ /// <see cref="SocketType.Dgram"/>-based client, this method configures the default remote host, and the client
+ /// will ignore any packets not coming from this default host (i.e the given <paramref name="remoteEndPoint"/>).
+ /// </summary>
+ /// <param name="remoteEndPoint">The remote end point which to which to connect the client.</param>
+ /// <param name="cancellationToken">
+ /// The <see cref="CancellationToken"/> upon whose cancellation the connection attempt should be aborted.
+ /// </param>
+ /// <returns>A <see cref="ValueTask"/> representing the connection attempt.</returns>
public ValueTask ConnectAsync(in EndPoint remoteEndPoint, CancellationToken cancellationToken = default)
{
TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();
@@ -79,9 +102,9 @@ namespace NetSharp.Sockets
AsyncCancellationToken cancellationArgs = (AsyncCancellationToken)token;
Socket.CancelConnectAsync(cancellationArgs.TransmissionArgs);
- }, new AsyncCancellationToken(in connection, in args));
+ }, new AsyncCancellationToken(in Connection, in args));
- if (connection.ConnectAsync(args)) return new ValueTask(tcs.Task);
+ if (Connection.ConnectAsync(args)) return new ValueTask(tcs.Task);
TransmissionArgsPool.Return(args);
diff --git a/NetSharp/NetSharp/Sockets/SocketConnection.cs b/NetSharp/NetSharp/Sockets/SocketConnection.cs
@@ -1,5 +1,4 @@
-using NetSharp.Packets;
-using NetSharp.Utils;
+using NetSharp.Utils;
using System;
using System.Buffers;
@@ -8,56 +7,120 @@ using System.Net.Sockets;
namespace NetSharp.Sockets
{
- //TODO document class
+ /// <summary>
+ /// Abstract base class for clients and servers.
+ /// </summary>
+ /// TODO add access to socket options
public abstract class SocketConnection : IDisposable
{
- protected readonly Socket connection;
-
+ /// <summary>
+ /// The underlying <see cref="Socket"/> which provides access to network operations.
+ /// </summary>
+ protected readonly Socket Connection;
+
+ /// <summary>
+ /// Pools arrays to function as temporary buffers during network read/write operations.
+ /// </summary>
protected readonly ArrayPool<byte> BufferPool;
+ /// <summary>
+ /// Pools <see cref="SocketAsyncEventArgs"/> objects for use during network read/write operations and calls
+ /// to <see cref="Socket"/>.XXXAsync(<see cref="SocketAsyncEventArgs"/>) methods.
+ /// </summary>
protected readonly SlimObjectPool<SocketAsyncEventArgs> TransmissionArgsPool;
- protected SocketConnection(in AddressFamily connectionAddressFamily, in SocketType connectionSocketType, in ProtocolType connectionProtocolType)
+ /// <summary>
+ /// Constructs a new instance of the <see cref="SocketConnection"/> class.
+ /// </summary>
+ /// <param name="connectionAddressFamily">The address family for the underlying socket.</param>
+ /// <param name="connectionSocketType">The socket type for the underlying socket.</param>
+ /// <param name="connectionProtocolType">The protocol type for the underlying socket.</param>
+ /// <param name="maxPooledBufferLength">The maximum size of the buffers stored in the <see cref="BufferPool"/>.</param>
+ protected internal SocketConnection(in AddressFamily connectionAddressFamily, in SocketType connectionSocketType,
+ in ProtocolType connectionProtocolType, in int maxPooledBufferLength)
{
- connection = new Socket(connectionAddressFamily, connectionSocketType, connectionProtocolType);
+ Connection = new Socket(connectionAddressFamily, connectionSocketType, connectionProtocolType);
- BufferPool = ArrayPool<byte>.Create(NetworkPacket.TotalSize, 1000);
+ BufferPool = ArrayPool<byte>.Create(maxPooledBufferLength, 1000);
- TransmissionArgsPool = new SlimObjectPool<SocketAsyncEventArgs>(CreateTransmissionArgs, ResetTransmissionArgs, DestroyTransmissionArgs, CanTransmissionArgsBeReused);
+ TransmissionArgsPool = new SlimObjectPool<SocketAsyncEventArgs>(CreateTransmissionArgs,
+ ResetTransmissionArgs, DestroyTransmissionArgs, CanTransmissionArgsBeReused);
}
+ /// <summary>
+ /// Delegate method used to construct fresh <see cref="SocketAsyncEventArgs"/> instances for use in the
+ /// <see cref="TransmissionArgsPool"/>. The resulting instance should register <see cref="HandleIoCompleted"/>
+ /// as an event handler for the <see cref="SocketAsyncEventArgs.Completed"/> event.
+ /// </summary>
+ /// <returns>The configured <see cref="SocketAsyncEventArgs"/> instance.</returns>
protected abstract SocketAsyncEventArgs CreateTransmissionArgs();
+ /// <summary>
+ /// Delegate method used to reset used <see cref="SocketAsyncEventArgs"/> instances for later reuse by
+ /// the <see cref="TransmissionArgsPool"/>.
+ /// </summary>
+ /// <param name="args">The <see cref="SocketAsyncEventArgs"/> instance that should be reset.</param>
protected abstract void ResetTransmissionArgs(SocketAsyncEventArgs args);
+ /// <summary>
+ /// Delegate method used to check whether the given used <see cref="SocketAsyncEventArgs"/> instance can be reused
+ /// by the <see cref="TransmissionArgsPool"/>. If this method returns <c>true</c>, <see cref="ResetTransmissionArgs"/>
+ /// is called on the given <paramref name="args"/>. Otherwise, <see cref="DestroyTransmissionArgs"/> is called.
+ /// </summary>
+ /// <param name="args">The <see cref="SocketAsyncEventArgs"/> instance to check.</param>
+ /// <returns>Whether the given <paramref name="args"/> should be reset and reused, or should be destroyed.</returns>
protected abstract bool CanTransmissionArgsBeReused(in SocketAsyncEventArgs args);
+ /// <summary>
+ /// Delegate method to destroy used <see cref="SocketAsyncEventArgs"/> instances that cannot be reused by the
+ /// <see cref="TransmissionArgsPool"/>. This method should deregister <see cref="HandleIoCompleted"/> as an
+ /// event handler for the <see cref="SocketAsyncEventArgs.Completed"/> event.
+ /// </summary>
+ /// <param name="remoteConnectionArgs">The <see cref="SocketAsyncEventArgs"/> which should be destroyed.</param>
protected abstract void DestroyTransmissionArgs(SocketAsyncEventArgs remoteConnectionArgs);
+ /// <summary>
+ /// Delegate method to handle asynchronous network IO completion via the <see cref="SocketAsyncEventArgs.Completed"/> event.
+ /// </summary>
+ /// <param name="sender">The object which raised the event.</param>
+ /// <param name="args">The <see cref="SocketAsyncEventArgs"/> instance associated with the asynchronous network IO.</param>
protected abstract void HandleIoCompleted(object sender, SocketAsyncEventArgs args);
+ /// <summary>
+ /// Binds the underlying socket.
+ /// </summary>
+ /// <param name="localEndPoint">The end point to which the socket should be bound.</param>
public void Bind(in EndPoint localEndPoint)
{
- connection.Bind(localEndPoint);
+ Connection.Bind(localEndPoint);
}
+ /// <summary>
+ /// Shuts down the underlying socket.
+ /// </summary>
+ /// <param name="how">Which socket transmission functions should be shut down on the socket.</param>
public void Shutdown(SocketShutdown how)
{
try
{
- connection.Shutdown(how);
+ Connection.Shutdown(how);
}
catch (SocketException) { }
}
+ /// <summary>
+ /// Disposes of managed and unmanaged resources used by the <see cref="SocketConnection"/> class.
+ /// </summary>
+ /// <param name="disposing">Whether this call was made by a call to <see cref="Dispose()"/>.</param>
protected virtual void Dispose(bool disposing)
{
if (!disposing) return;
- connection.Close();
- connection.Dispose();
+ Connection.Close();
+ Connection.Dispose();
}
+ /// <inheritdoc />
public void Dispose()
{
Dispose(true);
diff --git a/NetSharp/NetSharp/Sockets/SocketServer.cs b/NetSharp/NetSharp/Sockets/SocketServer.cs
@@ -4,14 +4,26 @@ using System.Threading.Tasks;
namespace NetSharp.Sockets
{
- //TODO document class
+ /// <summary>
+ /// Abstract base class for servers.
+ /// </summary>
public abstract class SocketServer : SocketConnection
{
- protected SocketServer(in AddressFamily connectionAddressFamily, in SocketType connectionSocketType, in ProtocolType connectionProtocolType)
- : base(in connectionAddressFamily, in connectionSocketType, in connectionProtocolType)
+ /// <summary>
+ /// Constructs a new instance of the <see cref="SocketServer"/> class.
+ /// </summary>
+ /// <inheritdoc />
+ protected SocketServer(in AddressFamily connectionAddressFamily, in SocketType connectionSocketType,
+ in ProtocolType connectionProtocolType, in int maxPooledBufferLength) : base(in connectionAddressFamily,
+ in connectionSocketType, in connectionProtocolType, in maxPooledBufferLength)
{
}
+ /// <summary>
+ /// Runs the server, handling requests from clients, until the <paramref name="cancellationToken"/> has its cancellation requested.
+ /// </summary>
+ /// <param name="cancellationToken">The <see cref="CancellationToken"/> upon whose cancellation the server should shut down.</param>
+ /// <returns>A <see cref="Task"/> representing the server's execution.</returns>
public abstract Task RunAsync(CancellationToken cancellationToken = default);
}
}
\ No newline at end of file
diff --git a/NetSharp/NetSharp/Sockets/Stream/StreamSocketClient.cs b/NetSharp/NetSharp/Sockets/Stream/StreamSocketClient.cs
@@ -1,4 +1,5 @@
-using NetSharp.Utils;
+using NetSharp.Packets;
+using NetSharp.Utils;
using System;
using System.Net.Sockets;
@@ -7,16 +8,31 @@ using System.Threading.Tasks;
namespace NetSharp.Sockets.Stream
{
- //TODO fix memory leak issue
+ //TODO document
+ public readonly struct StreamSocketClientOptions
+ {
+ public static readonly StreamSocketClientOptions Defaults =
+ new StreamSocketClientOptions(NetworkPacket.TotalSize);
+
+ public readonly int PacketSize;
+
+ public StreamSocketClientOptions(int packetSize)
+ {
+ PacketSize = packetSize;
+ }
+ }
+
//TODO address the need to handle series of network packets, not just single packets
//TODO document class
public sealed class StreamSocketClient : SocketClient
{
- public StreamSocketClient(in AddressFamily connectionAddressFamily, in ProtocolType connectionProtocolType)
- : base(in connectionAddressFamily, SocketType.Stream, in connectionProtocolType)
+ public StreamSocketClient(in AddressFamily connectionAddressFamily, in ProtocolType connectionProtocolType,
+ in StreamSocketClientOptions? clientOptions = null) : base(in connectionAddressFamily, SocketType.Stream,
+ in connectionProtocolType, clientOptions?.PacketSize ?? StreamSocketClientOptions.Defaults.PacketSize)
{
}
+ /// <inheritdoc />
protected override SocketAsyncEventArgs CreateTransmissionArgs()
{
SocketAsyncEventArgs connectionArgs = new SocketAsyncEventArgs();
@@ -26,15 +42,18 @@ namespace NetSharp.Sockets.Stream
return connectionArgs;
}
+ /// <inheritdoc />
protected override void ResetTransmissionArgs(SocketAsyncEventArgs args)
{
}
+ /// <inheritdoc />
protected override bool CanTransmissionArgsBeReused(in SocketAsyncEventArgs args)
{
return true;
}
+ /// <inheritdoc />
protected override void DestroyTransmissionArgs(SocketAsyncEventArgs remoteConnectionArgs)
{
remoteConnectionArgs.Completed -= HandleIoCompleted;
@@ -42,6 +61,7 @@ namespace NetSharp.Sockets.Stream
remoteConnectionArgs.Dispose();
}
+ /// <inheritdoc />
protected override void HandleIoCompleted(object sender, SocketAsyncEventArgs args)
{
switch (args.LastOperation)
@@ -51,7 +71,7 @@ namespace NetSharp.Sockets.Stream
if (connectToken.CancellationToken.IsCancellationRequested)
{
- connection.Disconnect(true);
+ Connection.Disconnect(true);
connectToken.CompletionSource.SetCanceled();
}
@@ -120,7 +140,7 @@ namespace NetSharp.Sockets.Stream
args.SetBuffer(receivedBytes, expectedBytes - receivedBytes);
- connection.ReceiveAsync(args);
+ Connection.ReceiveAsync(args);
}
else
{
@@ -172,7 +192,7 @@ namespace NetSharp.Sockets.Stream
args.SetBuffer(sentBytes, remainingBytes - sentBytes);
- connection.SendAsync(args);
+ Connection.SendAsync(args);
}
else
{
@@ -199,7 +219,7 @@ namespace NetSharp.Sockets.Stream
public void Disconnect(bool allowSocketReuse)
{
- connection.Disconnect(allowSocketReuse);
+ Connection.Disconnect(allowSocketReuse);
}
public ValueTask DisconnectAsync(bool allowSocketReuse, CancellationToken cancellationToken = default)
@@ -211,7 +231,7 @@ namespace NetSharp.Sockets.Stream
args.DisconnectReuseSocket = allowSocketReuse;
args.UserToken = new AsyncOperationToken(in tcs, in cancellationToken);
- if (connection.DisconnectAsync(args)) return new ValueTask(tcs.Task);
+ if (Connection.DisconnectAsync(args)) return new ValueTask(tcs.Task);
TransmissionArgsPool.Return(args);
@@ -220,9 +240,9 @@ namespace NetSharp.Sockets.Stream
public TransmissionResult Receive(byte[] buffer, SocketFlags flags = SocketFlags.None)
{
- int receivedBytes = connection.Receive(buffer, flags);
+ int receivedBytes = Connection.Receive(buffer, flags);
- return new TransmissionResult(in buffer, in receivedBytes, connection.RemoteEndPoint);
+ return new TransmissionResult(in buffer, in receivedBytes, Connection.RemoteEndPoint);
}
public ValueTask<TransmissionResult> ReceiveAsync(Memory<byte> receiveBuffer, SocketFlags flags = SocketFlags.None,
@@ -237,7 +257,7 @@ namespace NetSharp.Sockets.Stream
args.SocketFlags = flags;
args.UserToken = new AsyncTransmissionToken(in tcs, in cancellationToken);
- if (connection.ReceiveAsync(args)) return new ValueTask<TransmissionResult>(tcs.Task);
+ if (Connection.ReceiveAsync(args)) return new ValueTask<TransmissionResult>(tcs.Task);
TransmissionResult result = new TransmissionResult(in args);
@@ -248,9 +268,9 @@ namespace NetSharp.Sockets.Stream
public TransmissionResult Send(byte[] buffer, SocketFlags flags = SocketFlags.None)
{
- int sentBytes = connection.Send(buffer, flags);
+ int sentBytes = Connection.Send(buffer, flags);
- return new TransmissionResult(in buffer, in sentBytes, connection.RemoteEndPoint);
+ return new TransmissionResult(in buffer, in sentBytes, Connection.RemoteEndPoint);
}
public ValueTask<TransmissionResult> SendAsync(Memory<byte> sendBuffer, SocketFlags flags = SocketFlags.None,
@@ -265,7 +285,7 @@ namespace NetSharp.Sockets.Stream
args.SocketFlags = flags;
args.UserToken = new AsyncTransmissionToken(in tcs, in cancellationToken);
- if (connection.SendToAsync(args)) return new ValueTask<TransmissionResult>(tcs.Task);
+ if (Connection.SendToAsync(args)) return new ValueTask<TransmissionResult>(tcs.Task);
TransmissionResult result = new TransmissionResult(in args);
diff --git a/NetSharp/NetSharp/Sockets/Stream/StreamSocketServer.cs b/NetSharp/NetSharp/Sockets/Stream/StreamSocketServer.cs
@@ -12,7 +12,7 @@ namespace NetSharp.Sockets.Stream
public readonly struct StreamSocketServerOptions
{
public static readonly StreamSocketServerOptions Defaults =
- new StreamSocketServerOptions(NetworkPacket.TotalSize, 8);
+ new StreamSocketServerOptions(NetworkPacket.TotalSize, Environment.ProcessorCount);
public readonly int PacketSize;
@@ -26,7 +26,6 @@ namespace NetSharp.Sockets.Stream
}
}
- //TODO fix memory leak issue
//TODO address the need to handle series of network packets, not just single packets
//TODO allow for the server to do more than just echo packets
//TODO document class
@@ -51,15 +50,16 @@ namespace NetSharp.Sockets.Stream
}
}
- public readonly StreamSocketServerOptions ServerOptions;
+ private readonly StreamSocketServerOptions serverOptions;
public StreamSocketServer(in AddressFamily connectionAddressFamily, in ProtocolType connectionProtocolType,
in StreamSocketServerOptions? serverOptions = null) : base(in connectionAddressFamily, SocketType.Stream,
- in connectionProtocolType)
+ in connectionProtocolType, serverOptions?.PacketSize ?? StreamSocketServerOptions.Defaults.PacketSize)
{
- ServerOptions = serverOptions ?? StreamSocketServerOptions.Defaults;
+ this.serverOptions = serverOptions ?? StreamSocketServerOptions.Defaults;
}
+ /// <inheritdoc />
protected override SocketAsyncEventArgs CreateTransmissionArgs()
{
SocketAsyncEventArgs connectionArgs = new SocketAsyncEventArgs();
@@ -69,15 +69,18 @@ namespace NetSharp.Sockets.Stream
return connectionArgs;
}
+ /// <inheritdoc />
protected override void ResetTransmissionArgs(SocketAsyncEventArgs args)
{
}
+ /// <inheritdoc />
protected override bool CanTransmissionArgsBeReused(in SocketAsyncEventArgs args)
{
return true;
}
+ /// <inheritdoc />
protected override void DestroyTransmissionArgs(SocketAsyncEventArgs remoteConnectionArgs)
{
remoteConnectionArgs.Completed -= HandleIoCompleted;
@@ -85,6 +88,7 @@ namespace NetSharp.Sockets.Stream
remoteConnectionArgs.Dispose();
}
+ /// <inheritdoc />
protected override void HandleIoCompleted(object sender, SocketAsyncEventArgs args)
{
switch (args.LastOperation)
@@ -112,16 +116,15 @@ namespace NetSharp.Sockets.Stream
private void Accept(SocketAsyncEventArgs acceptArgs)
{
- bool operationPending = connection.AcceptAsync(acceptArgs);
+ bool operationPending = Connection.AcceptAsync(acceptArgs);
- if (!operationPending)
- {
- SocketAsyncEventArgs newAcceptArgs = TransmissionArgsPool.Rent();
+ if (operationPending) return;
- Accept(newAcceptArgs); // start a new accept operation to not miss any clients
+ SocketAsyncEventArgs newAcceptArgs = TransmissionArgsPool.Rent();
- CompleteAccept(acceptArgs);
- }
+ Accept(newAcceptArgs); // start a new accept operation to not miss any clients
+
+ CompleteAccept(acceptArgs);
}
private void CompleteAccept(SocketAsyncEventArgs connectedClientArgs)
@@ -139,11 +142,11 @@ namespace NetSharp.Sockets.Stream
{
RemoteStreamClientToken clientToken = (RemoteStreamClientToken)clientArgs.UserToken;
- byte[] requestBuffer = BufferPool.Rent(ServerOptions.PacketSize);
+ byte[] requestBuffer = BufferPool.Rent(serverOptions.PacketSize);
Memory<byte> requestBufferMemory = new Memory<byte>(requestBuffer);
clientToken.RentedBuffer = requestBuffer;
- clientArgs.SetBuffer(clientToken.RentedBuffer, 0, ServerOptions.PacketSize);
+ clientArgs.SetBuffer(clientToken.RentedBuffer, 0, serverOptions.PacketSize);
bool operationPending = clientToken.ClientSocket.ReceiveAsync(clientArgs);
@@ -159,7 +162,7 @@ namespace NetSharp.Sockets.Stream
if (clientArgs.SocketError == SocketError.Success)
{
- if (clientArgs.BytesTransferred == ServerOptions.PacketSize)
+ if (clientArgs.BytesTransferred == serverOptions.PacketSize)
{
// buffer was fully received
@@ -168,7 +171,7 @@ namespace NetSharp.Sockets.Stream
// TODO implement actual request processing, not just an echo server
NetworkPacket response = request;
- byte[] responseBuffer = BufferPool.Rent(ServerOptions.PacketSize);
+ byte[] responseBuffer = BufferPool.Rent(serverOptions.PacketSize);
Memory<byte> responseBufferMemory = new Memory<byte>(responseBuffer);
NetworkPacket.Serialise(response, responseBufferMemory);
@@ -176,17 +179,17 @@ namespace NetSharp.Sockets.Stream
BufferPool.Return(receiveToken.RentedBuffer, true); // at this point the request buffer can be returned
receiveToken.RentedBuffer = responseBuffer;
- clientArgs.SetBuffer(responseBuffer, 0, ServerOptions.PacketSize);
+ clientArgs.SetBuffer(responseBuffer, 0, serverOptions.PacketSize);
Send(clientArgs);
}
- else if (ServerOptions.PacketSize > clientArgs.BytesTransferred && clientArgs.BytesTransferred > 0)
+ else if (serverOptions.PacketSize > clientArgs.BytesTransferred && clientArgs.BytesTransferred > 0)
{
// receive the remaining parts of the buffer
int receivedBytes = clientArgs.BytesTransferred;
- clientArgs.SetBuffer(receivedBytes, ServerOptions.PacketSize - receivedBytes);
+ clientArgs.SetBuffer(receivedBytes, serverOptions.PacketSize - receivedBytes);
Receive(clientArgs);
}
@@ -221,7 +224,7 @@ namespace NetSharp.Sockets.Stream
if (clientArgs.SocketError == SocketError.Success)
{
- if (clientArgs.BytesTransferred == ServerOptions.PacketSize)
+ if (clientArgs.BytesTransferred == serverOptions.PacketSize)
{
// buffer was fully sent
@@ -231,13 +234,13 @@ namespace NetSharp.Sockets.Stream
Receive(clientArgs);
}
- else if (ServerOptions.PacketSize > clientArgs.BytesTransferred && clientArgs.BytesTransferred > 0)
+ else if (serverOptions.PacketSize > clientArgs.BytesTransferred && clientArgs.BytesTransferred > 0)
{
// send the remaining parts of the buffer
int sentBytes = clientArgs.BytesTransferred;
- clientArgs.SetBuffer(sentBytes, ServerOptions.PacketSize - sentBytes);
+ clientArgs.SetBuffer(sentBytes, serverOptions.PacketSize - sentBytes);
Send(clientArgs);
}
@@ -262,11 +265,17 @@ namespace NetSharp.Sockets.Stream
TransmissionArgsPool.Return(clientArgs);
}
+ public ref readonly StreamSocketServerOptions ServerOptions
+ {
+ get { return ref serverOptions; }
+ }
+
+ /// <inheritdoc />
public override async Task RunAsync(CancellationToken cancellationToken = default)
{
- connection.Listen(100);
+ Connection.Listen(100);
- for (int i = 0; i < ServerOptions.ConcurrentAcceptCalls; i++)
+ for (int i = 0; i < serverOptions.ConcurrentAcceptCalls; i++)
{
SocketAsyncEventArgs acceptArgs = TransmissionArgsPool.Rent();
diff --git a/NetSharp/NetSharpExamples/Program.cs b/NetSharp/NetSharpExamples/Program.cs
@@ -1,6 +1,7 @@
#define TCP
//#undef TCP
+using NetSharp.Packets;
using NetSharp.Sockets.Datagram;
using NetSharp.Sockets.Stream;
using NetSharp.Utils;
@@ -14,9 +15,6 @@ using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
-using NetworkPacket = NetSharp.Packets.NetworkPacket;
-using SocketServer = NetSharp.Sockets.SocketServer;
-
namespace NetSharpExamples
{
internal class Program
@@ -229,9 +227,9 @@ namespace NetSharpExamples
try
{
#if TCP
- using SocketServer server = new StreamSocketServer(AddressFamily.InterNetwork, ProtocolType.Tcp);
+ using StreamSocketServer server = new StreamSocketServer(AddressFamily.InterNetwork, ProtocolType.Tcp);
#else
- using SocketServer server = new DatagramSocketServer(AddressFamily.InterNetwork, ProtocolType.Udp);
+ using DatagramSocketServer server = new DatagramSocketServer(AddressFamily.InterNetwork, ProtocolType.Udp);
#endif
server.Bind(in ServerEndPoint);