-
Notifications
You must be signed in to change notification settings - Fork 4.8k
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
[API Proposal]: add overloads with SocketAddress to Socket's SendTo and ReceiveFrom #87397
Comments
Tagging subscribers to this area: @dotnet/ncl Issue DetailsBackground and motivationThis is subset from #30797. There is long discussion about how to make sending and receiving UDP more efficient. Current overloads allocate a lot for each packet and that kills performance and it puts lot of pressure on GC. Garbage collection has significant impact on scenarios that are sensitive to timing - like multiplayer Unity games. API Proposalnamespace System.Net.Sockets;
public class Socket
{
+ public int ReceiveFrom(Span<byte> buffer, SocketFlags socketFlags, SocketAddress socketAddress);
+ public ValueTask<int> ReceiveFromAsync(Memory<byte> buffer, SocketFlags socketFlags, SocketAddress socketAddress, CancellationToken cancellationToken = default);
+. public int SendTo(ReadOnlySpan<byte> buffer, SocketFlags socketFlags, SocketAddress socketAddress);
+. public ValueTask<int> SendToAsync(ReadOnlyMemory<byte> buffer, SocketFlags socketFlags, SocketAddress socketAddress, CancellationToken cancellationToken = default);
} Instead of allocating and returning EndPoint, we can reuse API Usageexample of simple server that would process request - inspired by DNS server linked in #30797 SocketAddress sa = new SocketAddress(AddressFamily.InterNetworkV6);
while (doWork)
{
int receivedLength = s.ReceiveFrom(buffer, SocketFlags.None, sa);
response = processRequest(buffer.Slice(0, receivedLength);
s.SendTo(response, SocketFlags.None, sa);
} The async version would be basically same with addition of Alternative DesignsNo response RisksThis is low-level API intended for experienced users. While there is nothing particularly dangerous one should be very careful about life cycle of the
|
namespace System.Net.Sockets;
public partial class Socket
{
// public int ReceiveFrom(Span<byte> buffer, SocketFlags socketFlags, SocketAddress socketAddress);
// public ValueTask<int> ReceiveFromAsync(Memory<byte> buffer, SocketFlags socketFlags, SocketAddress socketAddress, CancellationToken cancellationToken = default);
public int SendTo(ReadOnlySpan<byte> buffer, SocketFlags socketFlags, SocketAddress socketAddress);
public ValueTask<int> SendToAsync(ReadOnlyMemory<byte> buffer, SocketFlags socketFlags, SocketAddress socketAddress, CancellationToken cancellationToken = default);
} |
Thinking about the ergonomics of receiving the This basically adds namespace System.Net.Sockets;
public partial class Socket
{
public int ReceiveFrom(Span<byte> buffer, SocketFlags socketFlags, ref SocketAddress socketAddress);
public ValueTask<int> ReceiveFromAsync(Memory<byte> buffer, SocketFlags socketFlags, ref SocketAddress socketAddress, CancellationToken cancellationToken = default);
} |
This has been considered, the problem is that |
@antonfirsov can you elaborate on that? I just tried the signature with this in SharpLab https://sharplab.io/#gist:14ff035d0a0dec97365a6d992b47dfd9. What am I missing? |
|
Thanks for indulging in my ignorance. Makes perfect sense now! I am excited to see Levi's idea that he started to propose during API review. |
Why is it a problem to mutate the SocketAddress? The whole point of this was to mutate it, thereby providing a way of achieving an allocation-free receive path. We have other methods that mutate arguments... why can't this one? SocketAddress is just a strongly-typed buffer. Mutating buffers in read/receive methods is how things work. |
during the meeting it came up that users might assume that ReceiveFrom would receive an address with the parameter and not expect for it to mutate. I general the problem wasn't that it is mutating, but that the name was misleading and therefor it was excluded for now. at least that is what I understood from the meeting. |
Thanks. To me that's an issue for documentation: the overload may write into the supplied buffers, both for the data received and for the address from which it was received. I'd really like for these overloads to be in .NET 8. @bartonjs, @terrajobst, can we revisit this at the beginning of the next API review? |
namespace System.Net.Sockets;
public partial class Socket
{
public int ReceiveFrom(Span<byte> buffer, SocketFlags socketFlags, SocketAddress receivedAddress);
public ValueTask<int> ReceiveFromAsync(Memory<byte> buffer, SocketFlags socketFlags, SocketAddress receivedAddress, CancellationToken cancellationToken = default);
// Approved already
// public int SendTo(ReadOnlySpan<byte> buffer, SocketFlags socketFlags, SocketAddress socketAddress);
// public ValueTask<int> SendToAsync(ReadOnlyMemory<byte> buffer, SocketFlags socketFlags, SocketAddress socketAddress, CancellationToken cancellationToken = default);
} |
Background and motivation
This is subset from #30797. There is long discussion about how to make sending and receiving UDP more efficient. Current overloads allocate a lot for each packet and that kills performance and it puts lot of pressure on GC. Garbage collection has significant impact on scenarios that are sensitive to timing - like multiplayer Unity games.
API Proposal
Instead of allocating and returning EndPoint, we can reuse
SocketAddress
and we would update it in place. Ideally using API proposed in #86872 but in worst case theSocketAddress
is already writable and we can play some internal tricks.API Usage
example of simple server that would process request - inspired by DNS server linked in #30797
The async version would be basically same with addition of
CancellationToken
. UnlikeReceiveFromAsync
that usedEndPoint
and returnsSocketReceiveFromResult
we would returnValueTask<int>
just like the synchronous version and we would updatesocketAddress
in-place.Alternative Designs
No response
Risks
This is low-level API intended for experienced users. While there is nothing particularly dangerous one should be very careful about life cycle of the
socketAddress
as it can be overwritten on subsequent use.The text was updated successfully, but these errors were encountered: