Skip to content

Commit

Permalink
Adds synchronous span APIs for datagram sockets.
Browse files Browse the repository at this point in the history
  • Loading branch information
PJB3005 committed Apr 27, 2021
1 parent 1c9e200 commit 0ebfed4
Show file tree
Hide file tree
Showing 5 changed files with 167 additions and 32 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 @@ -417,6 +419,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
110 changes: 110 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,48 @@ public int SendTo(byte[] buffer, EndPoint remoteEP)
return SendTo(buffer, 0, buffer != null ? buffer.Length : 0, SocketFlags.None, remoteEP);
}

public int SendTo(ReadOnlySpan<byte> buffer, EndPoint remoteEP)
{
return SendTo(buffer, SocketFlags.None, remoteEP);
}

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 +1806,74 @@ public int ReceiveFrom(byte[] buffer, ref EndPoint remoteEP)
return ReceiveFrom(buffer, 0, buffer != null ? buffer.Length : 0, SocketFlags.None, ref remoteEP);
}

public int ReceiveFrom(Span<byte> buffer, ref EndPoint remoteEP)
{
return ReceiveFrom(buffer, SocketFlags.None, ref remoteEP);
}

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, new ReadOnlySpan<byte>(buffer, 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, new Span<byte>(buffer, 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
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,20 @@ public override Task<int> ReceiveAsync(Socket s, ArraySegment<byte> buffer) =>
Task.Run(() => s.Receive((Span<byte>)buffer, SocketFlags.None));
public override Task<int> SendAsync(Socket s, ArraySegment<byte> buffer) =>
Task.Run(() => s.Send((ReadOnlySpan<byte>)buffer, SocketFlags.None));
public override Task<SocketReceiveFromResult> ReceiveFromAsync(Socket s, ArraySegment<byte> buffer,
EndPoint endPoint) =>
Task.Run(() =>
{
SocketFlags socketFlags = SocketFlags.None;
int received = s.ReceiveFrom((Span<byte>)buffer, socketFlags, ref endPoint);
return new SocketReceiveFromResult
{
ReceivedBytes = received,
RemoteEndPoint = endPoint,
};
});
public override Task<int> SendToAsync(Socket s, ArraySegment<byte> buffer, EndPoint endPoint) =>
Task.Run(() => s.SendTo((ReadOnlySpan<byte>)buffer, endPoint));
public override Task<SocketReceiveMessageFromResult> ReceiveMessageFromAsync(Socket s, ArraySegment<byte> buffer, EndPoint endPoint) =>
Task.Run(() =>
{
Expand All @@ -497,6 +511,7 @@ public override Task<SocketReceiveMessageFromResult> ReceiveMessageFromAsync(Soc
PacketInformation = ipPacketInformation
};
});

public override Task SendFileAsync(Socket s, string fileName, ArraySegment<byte> preBuffer, ArraySegment<byte> postBuffer, TransmitFileOptions flags) =>
Task.Run(() => s.SendFile(fileName, preBuffer, postBuffer, flags));
public override bool UsesSync => true;
Expand Down

0 comments on commit 0ebfed4

Please sign in to comment.