-
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
[API Proposal]: make SocketAddress more useful #86872
Comments
Tagging subscribers to this area: @dotnet/ncl Issue DetailsBackground and motivationThe SocketAddress exists for long time but it is pretty useless at this point. It allows to Create and Serialize However, as noted in #78757 there is no convenient access via To solve this limitation, Note that To be able to handle UDP processing efficiently, #30797 is proposing to add overloads with Lastly, #86513 noted some issues around using API Proposalnamespace System.Net;
public class SocketAddress
{
- public int Size { get { throw null; } }
+ public int Size { get { throw null; } set { } }
+ public static int GetSocketAddressSize(AddressFamily addressFamily) { get { throw null; } }
+ public System.Memory<byte> SocketBuffer { get { throw null; } set { } }
+ public bool TryGetAddress(out System.Int128 address, out long scopeid) { throw null; }
+ public bool TryGetPort(out int port) { throw null; }
+ public bool TryWriteAddressBytes(Span<byte> destination, out int bytesWritten) { throw null; }
} API UsageSocketAddress sa = new SocketAddress(socket.AddressFamily)
byte[] buffer = new byte[MaxBuffer];
while (true)
{
int len = socket.ReceiveFrom(buffer, SocketFlags.None, sa);
if (len > 0)
{
ProcessMessage(buffer, sa.SocketBuffer.Span);
}
}
unsafe void ProcessMessage(byte[] buffer, readOnlySpan<byte> socketAddressBuffer)
{
fixed (void *ptr = socketAddressBuffer)
{
p/invoke to native with ptr
}
...
...
} Crucial part is ability to get/set the bytes efficiently and ability to shrink the address. The current assumption is that setting on SocketAddress sa = new SocketAddress(AddressFamily. InterNetworkV6)
/// native receive call ....
if (sa.Family == AddressFamily.InterNetwork)
{
sa.Size = GetSocketAddressSize(AddressFamily. InterNetwork);
}
All three method above are for convenience and could be implemented externally if needed. While we already have implementation and IP protocol seems vastly prevalent they can be useful for anybody who wants to avoid allocation of IPEndPoint just to do logging or some trivial functionality. Alternative DesignsAs noted above, #78993 may be good alternative in many cases. However it would not be sufficient for the new UDP methods as that plan is to avoid RisksThe primarily goal is to provide sufficient surface that we already have internally.
|
AFAIR SocketAddress does not store a port, you need to use an IPEndPoint for that? |
It does for IPv4/6: https://man7.org/linux/man-pages/man7/ipv6.7.html https://man7.org/linux/man-pages/man7/ip.7.html
|
@wfurt the usage example only demonstrates the part of the proposed API that is intended to be consumed by end users. It would be more interesting to see how socket code implementing #87397 will interact with the new methods. Can you share your POC for that? Some specific questions:
|
The setter for And I don't plan to allow buffer growing. Attempt to grow size beyond buffer size would throw. We really only need to string it down in certain cases - like UDS. |
Considering that we disallow growing the buffer, wouldn't it be then better to pass the |
we could. I feel that the setter is more flexible but that is also something API review may care about. (there is already internal constructor that takes Span but that allocates the array internally and it it does data copy) On the other hand, this is not critical for any work I plan for 8.0 so we can also take it out for now and do it later as needed. |
One idea during the 13Jul API review of #87397 in which reservations were made about mutating the It seems like that could blend well with this API proposal. public class SocketAddress
{
// Remove from the proposal
- public System.Memory<byte> SocketBuffer { get; set; }
+ public struct Buffer
+ {
+ public System.Memory<byte> Value { get; }
+ public SocketAddress Address { get; }
+ }
+ public SocketAddress.Buffer Buffer { get; }
}
the public int ReceiveFrom(Span<byte> buffer, SocketFlags socketFlags, SocketAddress.Buffer socketAddressBuffer); |
namespace System.Net;
-public class SocketAddress
+public class SocketAddress : IEquatable<SocketAddress>
{
- public int Size { get; }
+ public int Size { get; set; }
+ public static int GetMaximumAddressSize(AddressFamily addressFamily);
+ public Memory<byte> Buffer { get; }
} |
Background and motivation
The SocketAddress exists for long time but it is pretty useless at this point. It allows to Create and Serialize
EndPoint
to buffer that is passable to operation system and it allows read/write access to underlying buffer via indexing.However, as noted in #78757 there is no convenient access via
Span
orMemory
and there is no way how to pin the buffer so it can actually (via handle orfixed
) be passable to OS call.To solve this limitation,
Socket
code useInternals.SocketAddress
and there are extra steps and allocations to convert fromEndPoint
to buffer and we do sometimes extra processing to covertInternals.SocketAddress
toSocketAddress
to get the actualEndPoint
. That brings unnecessary allocations and work to coreSocket
functions. It would be really nice to avoid all this and simply use public API onSocketAddress
to do everything we need. While #78993 would help in many casesSocket
code also needs to deal with dual mode sockets and mapping between IP address families.Note that
SocketAddress
lives inSystem.Net.Primitives
andSystem.Net.Socket
code does not have convenient access to internal methods.To be able to handle UDP processing efficiently, #30797 is proposing to add overloads with
SocketAddress
and it would be nice to expose some of the functionality thatSocket
currently has.Lastly, #86513 noted some issues around using
GCHandle
. It would be nice to be able to useNativeMemory
in some cases to avoid pinning in managed code.API Proposal
This is optional part to make some convenience helper functions. For
Socket
refactoring we can use private attic extension e.g. ability to have efficient access to underlying data and ability to shrink given buffer to actual addresses is sufficient.SocketAddress
already hasEquals
method e.g. theIEquatable
is just to make it more obvious. Currently, it can only find equality with anotherSocketAddress
. We could also make it work for knownEndPoint
type and use allocatingSerialize
method toSocketAddress
as fallback.API Usage
UDP server
UDP client
Crucial part is ability to get/set the bytes efficiently and ability to shrink the address. The current assumption is that setting
Size
would not allow to set it to something bigger than allocated buffer e.g. string the address.on
DualMode
Socket
we can receive IPv4 or IPv6 address. We need space for either one but at the end we may need less than allocated. It may be OK to pass more data toCreate
method but it feels it would be nice to have the prices length as the OS functions would typically provide that.TryGetPort
make sense only IP protocol at the moment.TryGetAddress
is similar but it may return something in future - like forVSock
orNetlink
where the address is process ID.TryWriteAddressBytes
follow similar similar method onIPAddress
. forUnixDomainSocket
it would return thesun_path
ofsockaddr_un
e.g. it can be useful even in cases when address is not in numerical form.All three method above are for convenience and could be implemented externally if needed. While we already have implementation and IP protocol seems vastly prevalent they can be useful for anybody who wants to avoid allocation of IPEndPoint just to do logging or some trivial functionality.
Alternative Designs
As noted above, #78993 may be good alternative in many cases. However it would not be sufficient for the new UDP methods as that plan is to avoid
EndPoint
completely as well as it does help with mappings for dual mode.Risks
The primarily goal is to provide sufficient surface that we already have internally.
The text was updated successfully, but these errors were encountered: