-
Notifications
You must be signed in to change notification settings - Fork 4.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
add Span overloads for Socket datagram functions #938
Comments
Thanks, @wfurt. What about the asynchronous equivalents? Do we want to have |
I'm not sure. I look at SendAsync and ReceiveFromAsync as example and I only saw overload with SocketAsyncEventArgs. My general thinking was to mimic existing stream options. |
For historical reasons, the Task/ValueTask-based overloads are extension methods here: |
updated. I missed that part when looking at Socket documentation. I was not sure if we also need overload for ReceiveMessageFrom(Async). With ipPacketInformation it probably won't be good fit for anybody looking for performance. |
Is there any plan to avoid allocating EndPoint objects in these APIs? Since SendTo/ReceiveFrom APIs are called per datagram send/receive operation and they're probably on the hot path of a UDP server, even a small allocation could cause some kind of performance impact. |
Not critical for 3.0, pushing to Future milestone. |
Can we drop the two methods without public partial class Socket : IDisposable
{
public int SendTo(ReadOnlySpan<byte> buffer, SocketFlags socketFlags, EndPoint remoteEP);
public int ReceiveFrom(Span<byte> buffer, SocketFlags socketFlags, ref EndPoint remoteEP);
} For async, these would be more consistent with existing public static class SocketTaskExtensions
{
public static ValueTask<int> SendToAsync(this Socket socket, ReadOnlyMemory<byte> buffer, SocketFlags socketFlags, EndPoint remoteEP, CancellationToken cancellationToken = null);
public static ValueTask<SocketReceiveFromResult> ReceiveFromAsync(this Socket socket, Memory<byte> buffer, SocketFlags socketFlags, EndPoint remoteEP, CancellationToken cancellationToken = null);
public static ValueTask<SocketReceiveMessageFromResult> ReceiveMessageFromAsync(this Socket socket, Memory<byte> buffer, SocketFlags socketFlags, EndPoint remoteEP, CancellationToken cancellationToken = null);
} (note return values to get endpoint / packet info, and defaulted cancellation token as last param) |
I also have a use case for these endpoints in some UDP code implementing a subset of RTP transmission, which runs on a 20ms timer. My data frames are less than 1kbytes total, and I believe implementing this as a Span operation would be much more efficient than my current implementation, which rents a buffer from |
triage: maybe default the socket flags, and then ready for review. |
namespace System.Net.Sockets
{
// Open question: consider defaulting SocketFlags to SocketFlags.None to reduce API space? Might challenge IntelliSense.
public partial class Socket : IDisposable
{
// existing: public int SendTo(byte[] buffer, EndPoint remoteEP);
public int SendTo(ReadOnlySpan<byte> buffer, EndPoint remoteEP);
// existing: public int SendTo(byte[] buffer, SocketFlags socketFlags, EndPoint remoteEP);
public int SendTo(ReadOnlySpan<byte> buffer, SocketFlags socketFlags, EndPoint remoteEP);
// existing: public int ReceiveFrom(byte[] buffer, ref EndPoint remoteEP);
public int ReceiveFrom(Span<byte> buffer, ref EndPoint remoteEP);
// existing: public int ReceiveFrom(byte[] buffer, int offset, int size, SocketFlags socketFlags, ref EndPoint remoteEP);
public int ReceiveFrom(Span<byte> buffer, SocketFlags socketFlags, ref EndPoint remoteEP);
}
public static class SocketTaskExtensions
{
// existing: public static Task<int> SendToAsync(this Socket socket, ArraySegment<byte> buffer, SocketFlags socketFlags, EndPoint remoteEP);
public static ValueTask<int> SendToAsync(this Socket socket, ReadOnlyMemory<byte> buffer, EndPoint remoteEP, CancellationToken cancellationToken = null);
public static ValueTask<int> SendToAsync(this Socket socket, ReadOnlyMemory<byte> buffer, SocketFlags socketFlags, EndPoint remoteEP, CancellationToken cancellationToken = null);
// existing: public static Task<SocketReceiveFromResult> ReceiveFromAsync(this Socket socket, ArraySegment<byte> buffer, SocketFlags socketFlags, EndPoint remoteEndPoint);
public static ValueTask<SocketReceiveFromResult> ReceiveFromAsync(this Socket socket, Memory<byte> buffer, EndPoint remoteEP, CancellationToken cancellationToken = null);
public static ValueTask<SocketReceiveFromResult> ReceiveFromAsync(this Socket socket, Memory<byte> buffer, SocketFlags socketFlags, EndPoint remoteEP, CancellationToken cancellationToken = null);
// existing: public static Task<SocketReceiveMessageFromResult> ReceiveMessageFromAsync(this Socket socket, ArraySegment<byte> buffer, SocketFlags socketFlags, EndPoint remoteEndPoint);
public static ValueTask<SocketReceiveMessageFromResult> ReceiveMessageFromAsync(this Socket socket, Memory<byte> buffer, EndPoint remoteEP, CancellationToken cancellationToken = null);
public static ValueTask<SocketReceiveMessageFromResult> ReceiveMessageFromAsync(this Socket socket, Memory<byte> buffer, SocketFlags socketFlags, EndPoint remoteEP, CancellationToken cancellationToken = null);
}
} |
Since we are adding span overloads to For example, these APIs (+ overloads): public IAsyncResult BeginReceive(byte[] buffer, int offset, int size, SocketFlags socketFlags, AsyncCallback callback, object state);
public IAsyncResult BeginReceiveMessageFrom(byte[] buffer, int offset, int size, SocketFlags socketFlags, ref EndPoint remoteEP, AsyncCallback callback, object state);
public IAsyncResult BeginSend(byte[] buffer, int offset, int size, SocketFlags socketFlags, out SocketError errorCode, AsyncCallback callback, object state);
public IAsyncResult BeginSendTo(byte[] buffer, int offset, int size, SocketFlags socketFlags, EndPoint remoteEP, AsyncCallback callback, object state); |
If we have public partial interface IClient //regardless connection-oriented or connectionless.
{
ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default);
ValueTask<int> WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = default);
} Pretty cool! Can not use these two overloads in UDP if I don't have a default remote host: public static ValueTask<int> ReceiveAsync(this Socket socket, Memory<byte> buffer, SocketFlags socketFlags, CancellationToken cancellationToken = default);
public static ValueTask<int> SendAsync(this Socket socket, ReadOnlyMemory<byte> buffer, SocketFlags socketFlags, CancellationToken cancellationToken = default); need a |
Closing for mega-issue #33418. |
- BandwidthPriorities enum to not have a fake value for the length. Updated associated logic to use the maximum enum value + 1. - Fixed logic that converts NetSendFlags to BandwidthPriorities. It wasn't ignoring the Reiable/Droppable/Urgent flags properly. Also, changed the logic so that it doesn't run for Ack and Reliable packets since those were overwriting the value anyway. - Changed the 08 packets to be sent reliably. This is different than what ASSS does. I'm not sure if this is right and will need to investigate further. - DumpPk method to output the text representation in its own column properly for lines that have < 16 bytes. - Added an overload for SendToOne which takes a Span. Found that Socket.SendTo does not allow passing a Span yet, but it looks like Microsoft may add it eventually (dotnet/runtime#938). If they do, then it might be possible to switched everything from byte[] and ArraySegment<byte> to Span<byte>. Right now, the extra copy to a byte[] defeats the purpose.
Spanification, ValueTask, and cancellation
This proposal spanifies connectionless methods on
Socket
, uses aValueTask
return, and adds cancellation support. There are already such overloads in Send() and Receive() functions for connection oriented sockets (TCP) and this is complement for UDP.Proposed APIs
Most of the underlying code is already ready, this is about exposing it as public API.
cc: @stephentoub
The text was updated successfully, but these errors were encountered: