Skip to content

Commit

Permalink
Adds synchronous span APIs for datagram sockets. (#51956)
Browse files Browse the repository at this point in the history
* Adds synchronous span APIs for datagram sockets.

Part of #33418/#938

* Doc comments for new APIs.

* Fix review nits.

* Add 0-byte send tests.
  • Loading branch information
PJB3005 authored May 10, 2021
1 parent 1197851 commit dcb42e8
Show file tree
Hide file tree
Showing 6 changed files with 312 additions and 34 deletions.
4 changes: 4 additions & 0 deletions src/libraries/System.Net.Sockets/ref/System.Net.Sockets.cs
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,8 @@ public void Listen(int backlog) { }
public int ReceiveFrom(byte[] buffer, int size, System.Net.Sockets.SocketFlags socketFlags, ref System.Net.EndPoint remoteEP) { throw null; }
public int ReceiveFrom(byte[] buffer, ref System.Net.EndPoint remoteEP) { throw null; }
public int ReceiveFrom(byte[] buffer, System.Net.Sockets.SocketFlags socketFlags, ref System.Net.EndPoint remoteEP) { throw null; }
public int ReceiveFrom(System.Span<byte> buffer, ref System.Net.EndPoint remoteEP) { throw null; }
public int ReceiveFrom(System.Span<byte> buffer, System.Net.Sockets.SocketFlags socketFlags, ref System.Net.EndPoint remoteEP) { throw null; }
public System.Threading.Tasks.Task<System.Net.Sockets.SocketReceiveFromResult> ReceiveFromAsync(System.ArraySegment<byte> buffer, System.Net.Sockets.SocketFlags socketFlags, System.Net.EndPoint remoteEndPoint) { throw null; }
public System.Threading.Tasks.ValueTask<System.Net.Sockets.SocketReceiveFromResult> ReceiveFromAsync(System.Memory<byte> buffer, System.Net.Sockets.SocketFlags socketFlags, System.Net.EndPoint remoteEndPoint, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
public bool ReceiveFromAsync(System.Net.Sockets.SocketAsyncEventArgs e) { throw null; }
Expand Down Expand Up @@ -419,6 +421,8 @@ public void SendFile(string? fileName, System.ReadOnlySpan<byte> preBuffer, Syst
public int SendTo(byte[] buffer, int size, System.Net.Sockets.SocketFlags socketFlags, System.Net.EndPoint remoteEP) { throw null; }
public int SendTo(byte[] buffer, System.Net.EndPoint remoteEP) { throw null; }
public int SendTo(byte[] buffer, System.Net.Sockets.SocketFlags socketFlags, System.Net.EndPoint remoteEP) { throw null; }
public int SendTo(System.ReadOnlySpan<byte> buffer, System.Net.EndPoint remoteEP) { throw null; }
public int SendTo(System.ReadOnlySpan<byte> buffer, System.Net.Sockets.SocketFlags socketFlags, System.Net.EndPoint remoteEP) { throw null; }
public System.Threading.Tasks.Task<int> SendToAsync(System.ArraySegment<byte> buffer, System.Net.Sockets.SocketFlags socketFlags, System.Net.EndPoint remoteEP) { throw null; }
public System.Threading.Tasks.ValueTask<int> SendToAsync(System.ReadOnlyMemory<byte> buffer, System.Net.Sockets.SocketFlags socketFlags, System.Net.EndPoint remoteEP, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
public bool SendToAsync(System.Net.Sockets.SocketAsyncEventArgs e) { throw null; }
Expand Down
148 changes: 148 additions & 0 deletions src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1380,6 +1380,67 @@ public int SendTo(byte[] buffer, EndPoint remoteEP)
return SendTo(buffer, 0, buffer != null ? buffer.Length : 0, SocketFlags.None, remoteEP);
}

/// <summary>
/// Sends data to the specified endpoint.
/// </summary>
/// <param name="buffer">A span of bytes that contains the data to be sent.</param>
/// <param name="remoteEP">The <see cref="EndPoint"/> that represents the destination for the data.</param>
/// <returns>The number of bytes sent.</returns>
/// <exception cref="ArgumentNullException"><c>remoteEP</c> is <see langword="null" />.</exception>
/// <exception cref="SocketException">An error occurred when attempting to access the socket.</exception>
/// <exception cref="ObjectDisposedException">The <see cref="Socket"/> has been closed.</exception>
public int SendTo(ReadOnlySpan<byte> buffer, EndPoint remoteEP)
{
return SendTo(buffer, SocketFlags.None, remoteEP);
}

/// <summary>
/// Sends data to a specific endpoint using the specified <see cref="SocketFlags"/>.
/// </summary>
/// <param name="buffer">A span of bytes that contains the data to be sent.</param>
/// <param name="socketFlags">A bitwise combination of the <see cref="SocketFlags"/> values.</param>
/// <param name="remoteEP">The <see cref="EndPoint"/> that represents the destination for the data.</param>
/// <returns>The number of bytes sent.</returns>
/// <exception cref="ArgumentNullException"><c>remoteEP</c> is <see langword="null" />.</exception>
/// <exception cref="SocketException">An error occurred when attempting to access the socket.</exception>
/// <exception cref="ObjectDisposedException">The <see cref="Socket"/> has been closed.</exception>
public int SendTo(ReadOnlySpan<byte> buffer, SocketFlags socketFlags, EndPoint remoteEP)
{
ThrowIfDisposed();
if (remoteEP == null)
{
throw new ArgumentNullException(nameof(remoteEP));
}

ValidateBlockingMode();

Internals.SocketAddress socketAddress = Serialize(ref remoteEP);

int bytesTransferred;
SocketError errorCode = SocketPal.SendTo(_handle, buffer, socketFlags, socketAddress.Buffer, socketAddress.Size, out bytesTransferred);

// Throw an appropriate SocketException if the native call fails.
if (errorCode != SocketError.Success)
{
UpdateSendSocketErrorForDisposed(ref errorCode);

UpdateStatusAfterSocketErrorAndThrowException(errorCode);
}
else if (SocketsTelemetry.Log.IsEnabled())
{
SocketsTelemetry.Log.BytesSent(bytesTransferred);
if (SocketType == SocketType.Dgram) SocketsTelemetry.Log.DatagramSent();
}

if (_rightEndPoint == null)
{
// Save a copy of the EndPoint so we can use it for Create().
_rightEndPoint = remoteEP;
}

return bytesTransferred;
}

// Receives data from a connected socket.
public int Receive(byte[] buffer, int size, SocketFlags socketFlags)
{
Expand Down Expand Up @@ -1764,6 +1825,93 @@ public int ReceiveFrom(byte[] buffer, ref EndPoint remoteEP)
return ReceiveFrom(buffer, 0, buffer != null ? buffer.Length : 0, SocketFlags.None, ref remoteEP);
}

/// <summary>
/// Receives a datagram into the data buffer and stores the endpoint.
/// </summary>
/// <param name="buffer">A span of bytes that is the storage location for received data.</param>
/// <param name="remoteEP">An <see cref="EndPoint"/>, passed by reference, that represents the remote server.</param>
/// <returns>The number of bytes received.</returns>
/// <exception cref="ArgumentNullException"><c>remoteEP</c> is <see langword="null" />.</exception>
/// <exception cref="SocketException">An error occurred when attempting to access the socket.</exception>
/// <exception cref="ObjectDisposedException">The <see cref="Socket"/> has been closed.</exception>
public int ReceiveFrom(Span<byte> buffer, ref EndPoint remoteEP)
{
return ReceiveFrom(buffer, SocketFlags.None, ref remoteEP);
}

/// <summary>
/// Receives a datagram into the data buffer, using the specified <see cref="SocketFlags"/>, and stores the endpoint.
/// </summary>
/// <param name="buffer">A span of bytes that is the storage location for received data.</param>
/// <param name="socketFlags">A bitwise combination of the <see cref="SocketFlags"/> values.</param>
/// <param name="remoteEP">An <see cref="EndPoint"/>, passed by reference, that represents the remote server.</param>
/// <returns>The number of bytes received.</returns>
/// <exception cref="ArgumentNullException"><c>remoteEP</c> is <see langword="null" />.</exception>
/// <exception cref="SocketException">An error occurred when attempting to access the socket.</exception>
/// <exception cref="ObjectDisposedException">The <see cref="Socket"/> has been closed.</exception>
public int ReceiveFrom(Span<byte> buffer, SocketFlags socketFlags, ref EndPoint remoteEP)
{
ThrowIfDisposed();
ValidateReceiveFromEndpointAndState(remoteEP, nameof(remoteEP));

SocketPal.CheckDualModeReceiveSupport(this);

ValidateBlockingMode();

// We don't do a CAS demand here because the contents of remoteEP aren't used by
// WSARecvFrom; all that matters is that we generate a unique-to-this-call SocketAddress
// with the right address family.
EndPoint endPointSnapshot = remoteEP;
Internals.SocketAddress socketAddress = Serialize(ref endPointSnapshot);
Internals.SocketAddress socketAddressOriginal = IPEndPointExtensions.Serialize(endPointSnapshot);

int bytesTransferred;
SocketError errorCode = SocketPal.ReceiveFrom(_handle, buffer, socketFlags, socketAddress.Buffer, ref socketAddress.InternalSize, out bytesTransferred);

UpdateReceiveSocketErrorForDisposed(ref errorCode, bytesTransferred);
// If the native call fails we'll throw a SocketException.
SocketException? socketException = null;
if (errorCode != SocketError.Success)
{
socketException = new SocketException((int)errorCode);
UpdateStatusAfterSocketError(socketException);
if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(this, socketException);

if (socketException.SocketErrorCode != SocketError.MessageSize)
{
throw socketException;
}
}
else if (SocketsTelemetry.Log.IsEnabled())
{
SocketsTelemetry.Log.BytesReceived(bytesTransferred);
if (SocketType == SocketType.Dgram) SocketsTelemetry.Log.DatagramReceived();
}

if (!socketAddressOriginal.Equals(socketAddress))
{
try
{
remoteEP = endPointSnapshot.Create(socketAddress);
}
catch
{
}
if (_rightEndPoint == null)
{
// Save a copy of the EndPoint so we can use it for Create().
_rightEndPoint = endPointSnapshot;
}
}

if (socketException != null)
{
throw socketException;
}

return bytesTransferred;
}

public int IOControl(int ioControlCode, byte[]? optionInValue, byte[]? optionOutValue)
{
ThrowIfDisposed();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1203,6 +1203,19 @@ public static SocketError SendTo(SafeSocketHandle handle, byte[] buffer, int off
return errorCode;
}

public static SocketError SendTo(SafeSocketHandle handle, ReadOnlySpan<byte> buffer, SocketFlags socketFlags, byte[] socketAddress, int socketAddressLen, out int bytesTransferred)
{
if (!handle.IsNonBlocking)
{
return handle.AsyncContext.SendTo(buffer, socketFlags, socketAddress, socketAddressLen, handle.SendTimeout, out bytesTransferred);
}

bytesTransferred = 0;
SocketError errorCode;
TryCompleteSendTo(handle, buffer, socketFlags, socketAddress, socketAddressLen, ref bytesTransferred, out errorCode);
return errorCode;
}

public static SocketError Receive(SafeSocketHandle handle, IList<ArraySegment<byte>> buffers, SocketFlags socketFlags, out int bytesTransferred)
{
SocketError errorCode;
Expand Down Expand Up @@ -1311,6 +1324,18 @@ public static SocketError ReceiveFrom(SafeSocketHandle handle, byte[] buffer, in
return completed ? errorCode : SocketError.WouldBlock;
}

public static SocketError ReceiveFrom(SafeSocketHandle handle, Span<byte> buffer, SocketFlags socketFlags, byte[] socketAddress, ref int socketAddressLen, out int bytesTransferred)
{
if (!handle.IsNonBlocking)
{
return handle.AsyncContext.ReceiveFrom(buffer, ref socketFlags, socketAddress, ref socketAddressLen, handle.ReceiveTimeout, out bytesTransferred);
}

SocketError errorCode;
bool completed = TryCompleteReceiveFrom(handle, buffer, socketFlags, socketAddress, ref socketAddressLen, out bytesTransferred, out socketFlags, out errorCode);
return completed ? errorCode : SocketError.WouldBlock;
}

public static SocketError WindowsIoctl(SafeSocketHandle handle, int ioControlCode, byte[]? optionInValue, byte[]? optionOutValue, out int optionLength)
{
// Three codes are called out in the Winsock IOCTLs documentation as "The following Unix IOCTL codes (commands) are supported." They are
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -300,31 +300,15 @@ public static unsafe SocketError SendFile(SafeSocketHandle handle, SafeFileHandl
}
}

public static unsafe SocketError SendTo(SafeSocketHandle handle, byte[] buffer, int offset, int size, SocketFlags socketFlags, byte[] peerAddress, int peerAddressSize, out int bytesTransferred)
public static SocketError SendTo(SafeSocketHandle handle, byte[] buffer, int offset, int size, SocketFlags socketFlags, byte[] peerAddress, int peerAddressSize, out int bytesTransferred) =>
SendTo(handle, buffer.AsSpan(offset, size), socketFlags, peerAddress, peerAddressSize, out bytesTransferred);

public static unsafe SocketError SendTo(SafeSocketHandle handle, ReadOnlySpan<byte> buffer, SocketFlags socketFlags, byte[] peerAddress, int peerAddressSize, out int bytesTransferred)
{
int bytesSent;
if (buffer.Length == 0)
fixed (byte* bufferPtr = &MemoryMarshal.GetReference(buffer))
{
bytesSent = Interop.Winsock.sendto(
handle,
null,
0,
socketFlags,
peerAddress,
peerAddressSize);
}
else
{
fixed (byte* pinnedBuffer = &buffer[0])
{
bytesSent = Interop.Winsock.sendto(
handle,
pinnedBuffer + offset,
size,
socketFlags,
peerAddress,
peerAddressSize);
}
bytesSent = Interop.Winsock.sendto(handle, bufferPtr, buffer.Length, socketFlags, peerAddress, peerAddressSize);
}

if (bytesSent == (int)SocketError.SocketError)
Expand Down Expand Up @@ -528,19 +512,16 @@ public static unsafe SocketError ReceiveMessageFrom(Socket socket, SafeSocketHan
return SocketError.Success;
}

public static unsafe SocketError ReceiveFrom(SafeSocketHandle handle, byte[] buffer, int offset, int size, SocketFlags socketFlags, byte[] socketAddress, ref int addressLength, out int bytesTransferred)
public static unsafe SocketError ReceiveFrom(SafeSocketHandle handle, byte[] buffer, int offset, int size, SocketFlags socketFlags, byte[] socketAddress, ref int addressLength, out int bytesTransferred) =>
ReceiveFrom(handle, buffer.AsSpan(offset, size), SocketFlags.None, socketAddress, ref addressLength, out bytesTransferred);

public static unsafe SocketError ReceiveFrom(SafeSocketHandle handle, Span<byte> buffer, SocketFlags socketFlags, byte[] socketAddress, ref int addressLength, out int bytesTransferred)
{
int bytesReceived;
if (buffer.Length == 0)
{
bytesReceived = Interop.Winsock.recvfrom(handle, null, 0, socketFlags, socketAddress, ref addressLength);
}
else

fixed (byte* bufferPtr = &MemoryMarshal.GetReference(buffer))
{
fixed (byte* pinnedBuffer = &buffer[0])
{
bytesReceived = Interop.Winsock.recvfrom(handle, pinnedBuffer + offset, size, socketFlags, socketAddress, ref addressLength);
}
bytesReceived = Interop.Winsock.recvfrom(handle, bufferPtr, buffer.Length, socketFlags, socketAddress, ref addressLength);
}

if (bytesReceived == (int)SocketError.SocketError)
Expand Down
Loading

0 comments on commit dcb42e8

Please sign in to comment.