Runtime: Better Socket Option and Network Interface Support.

Created on 14 Jul 2020  路  43Comments  路  Source: dotnet/runtime

Background and Motivation

Lost of application layer protocols such as RTSP require using TCP in different ways such as disabling TCP retransmission or using TCP Timestamp.

I might be on an mobile device plugged in and live streaming and I might want to get up and get in my car and continue the stream...

I must be able to tolerate network loss and re-connect as well as recover from RST for many reasons. Therefor I am likely using SendTo and ReceiveFrom always....

See also

Proposed API

//Because it is truly right and just, these should be intrinsics
public class Array{
   //Indicates if the array is null or empty (length == 0), outputs the length to len in the success case.
+ public static bool IsNullOrEmpty(Array array, out int len);
+ public static ToArray(params Object objects); //ToArray('',"",0)
+ public static ToArray<TElement>(params TElement elements); /// ToArray(1,2,3)
}

public class String{
   //Indicates if the string is null or empty (length == 0), outputs the length to len in the success case.
+ public static bool IsNullOrEmpty(String str, out int len);
+ public static bool IsNullOrEmptyOrWhitespace(String str, out int len);
}


public static class NetworkInformation{
+ public static bool TryGetMaximumTransmittableUnit(NetoworkInterface nic, out int mtu);
+ public static bool TrySetMaximumTransmittableUnit(NetworkInterface nic, int mtu);
//Would need to ensure it's valid by getting the NetworkInterface assoicated.
+ public static bool TryGetMaximumTransmittableUnit(System.Net.Sockets.Socket socket, out int mtu);
+ public static bool TrySetMaximumTransmittableUnit(System.Net.Sockets.Socket socket, int mtu);
}

+ public static class NetworkInterfaceExtensions {
+public static bool TryGetNetworkInterface(System.Net.IPAddress localAddress, out System.Net.NetworkInformation.NetworkInterface nic)
+public static System.Collections.Generic.IEnumerable<System.Net.NetworkInformation.NetworkInterface> GetNetworkInterface(System.Net.NetworkInformation.OperationalStatus status, params System.Net.NetworkInformation.NetworkInterfaceType[] interfaceTypes)
+  public static System.Collections.Generic.IEnumerable<System.Net.NetworkInformation.NetworkInterface> GetNetworkInterface(params System.Net.NetworkInformation.NetworkInterfaceType[] interfaceTypes)
+  public static bool TryGetNetworkInterface(System.Net.IPEndPoint localEndPoint, out System.Net.NetworkInformation.NetworkInterface nic)
+  public static bool  TryGetNetworkInterface(System.Net.Sockets.Socket socket, System.Net.NetworkInformation.NetworkInterface)
+ public static double GetSpeedInMBytesPerSecond(this System.Net.NetworkInformation.NetworkInterface networkInterface);
+ public static double CaulculateInterframeGapNanoseconds(long speedAsBitsPerSecond);
+  public static double GetInterframeGapNanoseconds(this System.Net.NetworkInformation.NetworkInterface networkInterface);
+ public static double GetInterframeGapMicroseconds(this System.Net.NetworkInformation.NetworkInterface networkInterface);
+ public static bool TryGetFirstMulticastIPAddress(System.Net.NetworkInformation.NetworkInterface networkInterface, System.Net.Sockets.AddressFamily addressFamily,  out System.Net.IPAddress ip)
+ public static bool TryGetFirstUnicastIPAddress(System.Net.NetworkInformation.NetworkInterface networkInterface, System.Net.Sockets.AddressFamily addressFamily, out System.Net.IPAddress ip)

//Counter support, Because bandwidth monitoring is applicable as well as SNMP v3 for security etc.
+ public BigInteger BytesSent System.Net.NetworkInformation.NetworkInterface networkInterface);
+ public BigInteger BytesReceived System.Net.NetworkInformation.NetworkInterface networkInterface);
+ public bool TrySetBytesReceived System.Net.NetworkInformation.NetworkInterface networkInterface, BigInteger value);
+ public bool TrySetBytesSent System.Net.NetworkInformation.NetworkInterface networkInterface, BigInteger value);
+ public bool TryResetBytesSent System.Net.NetworkInformation.NetworkInterface networkInterface);
+ public bool TryResetBytesReceived System.Net.NetworkInformation.NetworkInterface networkInterface);

+ public BigInteger BytesSent System.Net.NetworkInformation.NetworkInterface networkInterface, ProtocolType protocolType);
+ public BigInteger BytesReceived System.Net.NetworkInformation.NetworkInterface networkInterface, ProtocolType protocolType);

+ public bool TrySetBytesReceived System.Net.NetworkInformation.NetworkInterface networkInterface, , ProtocolType protocolType, BigInteger value);
+ public bool TrySetBytesSent System.Net.NetworkInformation.NetworkInterface networkInterface , ProtocolType protocolType, BigInteger value);
+ public bool TryResetBytesSent System.Net.NetworkInformation.NetworkInterface networkInterface , ProtocolType protocolType, BigInteger value);
+ public bool TryResetBytesReceived System.Net.NetworkInformation.NetworkInterface networkInterface , ProtocolType protocolType, BigInteger value);


+ }

+ public static class NetworkInformation.IP {
+public static System.Net.IPAddress GetFirstV4IPAddress();
+ public static System.Net.IPAddress GetFirstV6IPAddress();
+ public static System.Net.IPAddress GetFirstUnicastIPAddress(System.Net.Sockets.AddressFamily addressFamily)
+ public static System.Net.IPAddress GetFirstUnicastIPAddress(System.Net.Sockets.AddressFamily addressFamily, out System.Net.NetworkInformation.NetworkInterface networkInterface);
+public static System.Net.IPAddress GetFirstMulticastIPAddress(System.Net.Sockets.AddressFamily addressFamily, out System.Net.NetworkInformation.NetworkInterface networkInterface);
+public static System.Net.IPAddress GetFirstMulticastIPAddress(System.Net.Sockets.AddressFamily addressFamily);
+public static bool TrySetMulticastTimeToLive(this System.Net.Sockets.Socket socket, TimeSpan ttl, out int error);
+public static bool TryGetMulticastTimeToLive(this System.Net.Sockets.Socket socket, out TimeSpan ttl);
+public static bool TryFindOpenPort(System.Net.Sockets.ProtocolType type, int start = 30000, bool even = true, System.Net.IPAddress localIp = null, out int openPort);
+}

+ public static class NetworkInformation.Tcp{ public static TimeSpan DefaultRetransmissionTime{get;}}

+ public static class NetworkInformation.Udp{ public static int MaxFragmentSize {get;};}

//because technically tcp can multicast
+ public static class MulticastExtensions {
+public static void JoinMulticastGroup(this System.Net.Sockets.Socket socket, System.Net.IPAddress toJoin)
+public static void JoinMulticastGroup(this System.Net.Sockets.Socket socket, System.Net.IPAddress toJoin, int interfaceIndex)
+public static bool TryCreateMembershipAddress(System.Net.IPAddress localIp, System.Net.IPAddress multicastIp, System.Net.IPAddress sourceIp, out byte[] membershipAddress)
+public static bool TryJoinMulticastGroup(this System.Net.Sockets.Socket socket, System.Net.IPAddress toJoin, System.Net.IPAddress sourceIp, out byte[] membershipAddress)
//should also have index... Group_Req, MGroup_Req
+ public static bool TryLeaveMulticastGroup(this System.Net.Sockets.Socket socket, byte[] membershipAddress)
+ }

+ public static class TCPSocketExtensions {
+ public static bool TryChangeTcpKeepAlive(System.Net.Sockets.Socket socket, TimeSpan time, TimeSpan interval);
+ public static bool TryDisableTcpKeepAlive(System.Net.Sockets.Socket socket);

+        static readonly System.Net.Sockets.SocketOptionName TcpMaximumRetransmissionOptionName;
+ public static void SetMaximumTcpRetransmissionTime(System.Net.Sockets.Socket socket, int amountInSeconds = NetworkInformation.Tcp.DefaultRetransmissionTime);
+ public static bool TryDisableTcpRetransmissions(System.Net.Sockets.Socket socket);
+  public static bool TryEnableTcpRetransmissions(System.Net.Sockets.Socket socket);
+   public static bool TryGetMaximumTcpRetransmissionTime(System.Net.Sockets.Socket socket, out TimeSpan retransmissionTime);

+       static readonly System.Net.Sockets.SocketOptionName TcpMaximumSegmentSizeOptionName;
+        public static bool TryGetMaximumSegmentSize(System.Net.Sockets.Socket socket, out int result);
+        public static bool TrySetMaximumSegmentSize(System.Net.Sockets.Socket socket, int size);

//Will Remove?
+      const int NoSynRetries = 9;

//Will Remove but should not so if people want to patch they can...
+    static readonly System.Net.Sockets.SocketOptionName NoSynRetriesOption = (System.Net.Sockets.SocketOptionName)NoSynRetries;

+ public static bool TrySetTcpNoSynRetries(System.Net.Sockets.Socket socket, TimeSpan time = 1);
+ public static bool TryDisableTcpNoSynRetries(System.Net.Sockets.Socket socket);
+   public static bool TryEnableTcpNoSynRetries(System.Net.Sockets.Socket socket);

//See notes
+       const int Timestamp = 10;

//See Notes
+        static readonly System.Net.Sockets.SocketOptionName TimestampOption = (System.Net.Sockets.SocketOptionName)Timestamp;

+       public static bool TrySetTcpTimestamp(System.Net.Sockets.Socket socket, int value = 1);
+       public static bool TryGetTcpTimestamp(System.Net.Sockets.Socket socket, out int Timestamp);
+      public static bool DisableTcpTimestamp(System.Net.Sockets.Socket socket);
+      public static void EnableTcpTimestamp(System.Net.Sockets.Socket socket);       

//here also
+        public const int TcpOffloadNoPreference = 0;
+        public const int TcpOffloadNotPreferred = 1;
+        public const int TcpOffloadPreferred = 2;
+        const int TcpOffloadPreferenceWindows = 11;
+       static readonly System.Net.Sockets.SocketOptionName TcpOffloadPreferenceOption;
+public static bool SetTcpOffloadPreference(System.Net.Sockets.Socket socket, int value = TcpOffloadPreferred) ;
+ const int TcpCongestionAlgorithmWindows = 12;
//See notes
+        static readonly System.Net.Sockets.SocketOptionName TcpCongestionAlgorithmOption = (System.Net.Sockets.SocketOptionName)TcpCongestionAlgorithmWindows;

+        public static bool TrySetTcpCongestionAlgorithm(System.Net.Sockets.Socket socket, int value = 1);
+        public static bool TryDisableTcpCongestionAlgorithm(System.Net.Sockets.Socket socket);
+        public static bool TryEnableTcpCongestionAlgorithm(System.Net.Sockets.Socket socket, int algorithmType = 1);

//Should not remove this
+        static readonly System.Net.Sockets.SocketOptionName DelayFinAckOption;

+        public static void SetDelayedAckTimeout(System.Net.Sockets.Socket socket, TimeSpan time);
+        public static void DisableTcpDelayedAck(System.Net.Sockets.Socket socket);
+        public static void EnableTcpDelayedAck(System.Net.Sockets.Socket socket);
+        public static TimeSpan GetTcpDelayedAckTime(System.Net.Sockets.Socket socket);

+}

+ public static class SocketExtensions{

+ public static bool TryEnableTcpOutOfBandDataInLine(System.Net.Sockets.Socket socket);
+public static bool TryDisableTcpOutOfBandDataInLine(System.Net.Sockets.Socket socket);
+ public static bool IsTcpOutOfBandInLineEnabled(System.Net.Sockets.Socket socket);

+ public static bool TryEnableNagelAlgorithm(System.Net.Sockets.Socket socket);
+ public static bool TryDisableNagelAlgorithm(System.Net.Sockets.Socket socket);
+ public static bool TrySetTcpNoDelay(System.Net.Sockets.Socket socket, int amount = 1);

+ public static bool IsExpedited(System.Net.Sockets.Socket socket);
+ public static int GetExpeditedOption(System.Net.Sockets.Socket socket)
+ public static bool IsStandardUrgency(System.Net.Sockets.Socket socket, bool checkNotUrgent = true);
+ public static bool IsNotUrgent(System.Net.Sockets.Socket socket)
+ public static bool IsTcpStandardUrgency(System.Net.Sockets.Socket socket, bool checkNotUrgent = true);
+ public static bool IsTcpNotUrgent(System.Net.Sockets.Socket socket)


+        const int NotUrgent = 7;

+    static readonly System.Net.Sockets.SocketOptionName TcpNotUrgent;

+       const int StandardUrgency = 6;

+      static readonly  System.Net.Sockets.SocketOptionName TcpStandardUrgency;

+     internal static bool TrySetExpedited(System.Net.Sockets.Socket socket, int amount = 1, out int error);
+        public static bool TrySetNotUrgent(System.Net.Sockets.Socket socket, out int error);

+       public static bool TrySetTcpStandardUrgent(System.Net.Sockets.Socket socket, out int error);
+        public static bool TrySetTcpStandardUrgentEnableTcpExpedited(System.Net.Sockets.Socket socket, out int error)



+ public static void DisableLinger(System.Net.Sockets.Socket socket);

+        public static void EnableLinger(System.Net.Sockets.Socket socket);

+       public static bool  TryGetLingerOption(System.Net.Sockets.Socket socket, out System.Net.Sockets.LingerOption result);
+     public static bool TrySetLingerOption(System.Net.Sockets.Socket socket, System.Net.Sockets.LingerOption lingerOption);
+    public static bool TrySetLingerOption(System.Net.Sockets.Socket socket, bool enable, TimeSpan time)

+   public static bool TrySetRawOption(System.Net.Sockets.Socket socket, System.Net.Sockets.SocketOptionName name, ReadOnlySpan<byte> value);//buffer contains error code on failure
+   public static bool TryGetRawOption(System.Net.Sockets.Socket socket, System.Net.Sockets.SocketOptionName name, out ReadOnlySpan<byte>buffer); //buffer contains error code on failure

+ }

+ public class UDPSocketExtensions {
+ public static bool IsNoFragment(Socket socket);
+ public static bool TrySetNoFragment(Socket socket, int maxSize, out int errror); //0 for disabled
+ public static bool TryDisableNoFragment(Socket socket, out int errror);
+   public static bool TrySetRawOption(System.Net.Sockets.Socket socket, System.Net.Sockets.SocketOptionName name, ReadOnlySpan<byte> value);//buffer contains error code on failure
+   public static bool TryGetRawOption(System.Net.Sockets.Socket socket, System.Net.Sockets.SocketOptionName name, out ReadOnlySpan<byte>buffer); //buffer contains error code on failure
+ }

+ public static class IP4SocketExtensions {
+public static bool TryGetTypeOfService(Socket socket, out int tos);
+public static bool TrySetTypeOfService(Socket socket, int tos, out int error);
+public static bool TryGetTimeToLive(this System.Net.Sockets.Socket socket, out TimeSpan ttl);
+public static bool TrySetTimeToLive(this System.Net.Sockets.Socket socket, TimeSpan ttl, out int error);
+ }

+ public static class IP6SocketOptions{
+public enum IPOptionType{ 
+Unknown,
+Hop,
+Destination,
+Routing,
+Fragment,
+Authentication,
+EncapsulatingSecurityPayload,
+ Mobility,
+ HostIdentification,
+ Shim6,
+ Reserved1,
+ Reserved2,
+ }
+ public bool TryGetOptionCount(Socket socket, IPOptionType optionType, out int result)// set value on error or success
+ public bool TryGetOption(Socket socket, IPOptionType optionType, int index, out int result)// set value on error or success
+ public bool TrySetOption(Socket socket, IPOptionType optionType, int index, ref value)// set value on error or success
//
+ public bool TryGetRawOptionCount(Socket socket, int optionType, out int result)// set value on error or success
+ public bool TryGetRawOption(Socket socket, int optionType, int index, out int result)// set value on error or success
+ public bool TrySetRawOption(Socket socket, int optionType, int index, ref value)// set value on error or success
+}

public class Socket {
//Reserves port when true (will take longer depending on stack) I called this ReservePort but it was not my intention to run everywhere... This would need a structure of `SocketReservation{ SocketInformation}` in the worst case or the use of SO_REUSEADDR and creation and binding of the socket
+ public static bool TryCreate(System.Net.Sockets.SocketType socketType, System.Net.Sockets.ProtocolType protocol, System.Net.IPAddress localIp, int port, out System.Net.Sockets.Socket socket, bool reserve= false)
//because it's just
+ public static int SendTo(byte[] buffer, int offset, int size, System.Net.Sockets.Socket socket, System.Net.EndPoint remote, System.Net.Sockets.SocketFlags flags, out System.Net.Sockets.SocketError error);
+   public static bool TrySetRawOption(System.Net.Sockets.Socket socket,  System.Net.Sockets.SocketsOptionLevel level, System.Net.Sockets.SocketOptionName name, ReadOnlySpan<byte> value);//buffer contains error code on failure
+   public static bool TryGetRawOption(System.Net.Sockets.Socket socket, System.Net.Sockets.SocketsOptionLevel level, System.Net.Sockets.SocketOptionName name, out ReadOnlySpan<byte>buffer); //buffer contains error code on failure
}

I think connections should be something like this... I would love to work with you on it as I already had one in .net core MicroFramework, I can try to dig it up if you want but this is probably the same just without the client server piece.

```c#

using(var c = new NetworkConnection())
{
c.ConfigureSocket();
c.Bind();
c.Connect(someRemoteEndPoint);
}
```

Connection

NetworkConnection

ISocketReference

Prior Art

See https://github.com/juliusfriedman/net7mma/blob/master/Common/Extensions/SocketExtensions.cs, https://github.com/juliusfriedman/net7mma/blob/master/Common/Extensions/NetworkInterfaceExtensions.cs

Risks

Low as people are going to attempt to use GetRawSocketOption and SetRawSocketOption anyway AND there was an approved API for QUICK_ACK but it was removed until there was consensus and hopefully we can find that here.

api-suggestion area-System.Net.Sockets

Most helpful comment

Hi @juliusfriedman just to support @stephentoub comment -- in API proposals it's important to start off with with the specific API exactly as proposed, formatted per the template, with clear motivation, and answers to any questions that can be anticipated. That can avoid time consuming and expensive questions back and forth for all of us.

All 43 comments

Tagging subscribers to this area: @dotnet/ncl
Notify danmosemsft if you want to be subscribed.

cc @scalablecory as I think this will be useful from your Connection abstraction.

cc @danmosemsft

cc @antonfirsov

cc @stephentoub

@juliusfriedman can you help us out by cleaning this up to just the API surface? If there are any tricky implementation details (portability, etc.) they can be discussed in 2nd half of issue, or in further comments.

Sorry, will do so asap, let me clarify the ask; just remove the implementation details right and keep it to just the header definition style/ API surface? Also would you prefer the diff or c# highlighting?

@juliusfriedman can you provide a list of socket options (like: IPPROTO_TCP/TCP_MAXRT) you need, and whether they are supported on Windows/Linux?

It would be preferable to support these by extending the SocketOptionName enum, instead of using new methods.

+        internal static void SetTcpOption(System.Net.Sockets.Socket socket, System.Net.Sockets.SocketOptionName name, int value)
+        {
+          socket.SetRawSocketOption(System.Net.Sockets.SocketOptionLevel.Tcp, name, value);
+       }

note: this doesn't work. SetRawSocketOption can't accept a name. It's meant for socket options not part of SocketOptionName for which the user must provide the platform-specific optionLevel, optionName int values.

System.Net.Sockets.SocketOptionName is just an enum and can be cast to int

I have included them in the prior art everything seems supported after Timestamp (10) except 11, 12 which is congestion control, outside of that there is different values at USER_RT due to windows vs unix.

Other than that the options are mostly the same afaik.

System.Net.Sockets.SocketOptionName is just an enum and can be cast to int

The values of SocketOptionName (which come from Windows .NET Framework) match the raw values on Windows. Casting to int doesn't work cross-platform.
For example, DropMembership has a value of 13 in the enum. Where on Linux, it should be IP_DROP_MEMBERSHIP (value 36), or IPV6_DROP_MEMBERSHIP (value 21), depending on whether ipv6 is used.
If the SocketOptionName enum gets extended, new values may even no longer match on Windows.

Hence why they can be defined per OS at runtime similar to CPU stuff. I suggest you review the prior art and see why things were done the way they were.

You need to do it at runtime currently. When added to .NET Core, the values are captured as part of the build and will be mapped from SocketOptionName values to the appropriate platform-specific optionLevel, optionName internally.

You need to do it at runtime currently. When added to .NET Core, the values are captured as part of the build and will be mapped from SocketOptionName values to the appropriate platform-specific optionLevel, optionName internally.

So when I load a new kernel module with a new network interface after the runtime has booted what will happen? you will not support the newer adapter?

Actually it looks like you will support the new adapter but not any new options it adds... unless TryGetPlatformSocketOption is called again.

Doing this at runtime in the static constructor (or calling to an API from there) seems better as it would be closer to where the user is using the code and it would also likely mean that device in expected to be ready for the common case.

That still doesn't help if they remove that module after the fact.

None of this is important for Raw, UDP or TCP as most of those options math up to 10. it's the ones that dont which again are mostly udp and others.

Support for these socket options gets put into .NET Core at build time. It's unrelated to kernel modules.

If I load a module while an app is running and then use that newly added hardware.... Things like usb network adapters etc

@juliusfriedman, this has nothing to do with things being loaded while the app is running. The options we already support and would consider adding support for are ones defined in the relevant header files for networking stacks of the supported operating systems. Some options differ across OSes, either not existing at all in some, or existing with different numerical values, or existing with different names. As such, the .NET layer maintains a statically-defined mapping between the SocketOptionName/Level it exposes, and what it actually passes down to the OS under the covers when you use Get/SetSocketOption. If there are specific values you believe are a) important and b) missing, please list those as @tmds requested. For anything else, developers can use SetRawSocketOption to pass down the OS-specific values.

In such cases then if I update the kernel headers my app log changes?

I would expect it to be after the kernel has been compiled and probed from the kernel but whatever.

The problem I see with doing this only 1 time at runtime is many

1) I may never even use a socket in my program
2) My headers might change
3) My network stack might change and choose to apply socket options differently

With that typed, What is the ask here and I will oblige happily.

Is not my intention to :

Slow down the startup of the framework
Have a gap in the API such that the process has to be killed to speak a new protocol on a newly connected device

I may never even use a socket in my program

Then none of this matters.

My headers might change

Every time you compile a C/C++ application, it's baking in the values from the headers into the binary. If your OSes headers changed such that these values changed, that would be a massive breaking change, and every application and library used on that OS that used the old values would break. This is not something we're concerned about happening; if an OS did that, the ramifications extend very far.

My network stack might change and choose to apply socket options differently

Then you have a non-conforming networking stack.

non conforming to who how do you know if you cannot observe the difference?

non conforming to who how do you know if you cannot observe the difference?

You said "choose to apply socket options differently". I assumed you meant it had behavioral differences. If it has behavioral differences, then it's non-conforming to everyone trying to use it. If it doesn't have behavioral differences, then I don't understand what your concern is.

I don't follow your train of thought.... even if it had behavior differences but it had a big enough switch board to apply them in a way where it didn't effect previously created sockets it would not be a problem....

Lets keep this to what I need to do for the proposal and not try to muddy the waters with comp sci voodoo.

I don't care to argue the differences of local realism and locality here but this is a hidden variable problem nothing more.

I do not understand what you're saying or arguing for or against.

Lets keep this to what I need to do for the proposal

Please:

  • update the top post with the signatures of APIs you're proposing, with specific details about what problem each solves and why it's needed, scenarios it'll be used, etc.
  • do not include any implementation.
  • ensure that members have the visibility you're suggesting, e.g. if something doesn't have a visibility included, then it's not public, so it shouldn't be included at all.

I will get to this shortly. Thank you for the clarification

Hi @juliusfriedman just to support @stephentoub comment -- in API proposals it's important to start off with with the specific API exactly as proposed, formatted per the template, with clear motivation, and answers to any questions that can be anticipated. That can avoid time consuming and expensive questions back and forth for all of us.

@danmosemsft

I am sorry I did not have the ability to mark this item with ready for review or intend for it to be reviewed yet, it is not yet ready.

I was making it based on the linked discussion and was trying to get it into a proper proposal state to which I evolved form TCP to Socket in general to best support the features being proposed.

I will ping you guys again when this is ready as it's marked future anyway, I was doing this in haste because the other linked API was approved and I really didn't want to see that happen.

Please also feel free to open new issues as you see fit and close this issue (break it down)and reference this issue from there if you guys can do it faster / better than I can.

@juliusfriedman I see -- the way we work is, as soon as an issue is opened we begin looking at it. We don't have a concept of draft issues -- that is why we encourage folks to make them fully formed ideas with backing data so that we can give issues the attention they deserve. I'll close this for now, and you can reopen when you're ready. Feel free to edit the top post as needed.

I am sorry @danmosemsft. I did not mean to violate any process I just wanted to get the ball rolling so to type.

I will edit this this week, and weekend and cc you when it's better looking.

Sorry to anyone else whom I also might have offended.

You haven't offended anyone and are welcome here 馃樅

@danmosemsft, @jkotas

I also don't see an easy way to re-open? I am just about ready. I think so anyway...

There sounds be an option to reopen I thought, if you're the opener (?)..anyway, done.

I will let an expert give feedback.

BTW there is no need to @-mention people generally unless you need their specific attention. When issues get an area- tag the owners of that area will look at it. When you comment on an issue you are automatically subscribed to it so subsequent comments will by default notify anyone who has previously commented.

Sorry, I was @-mentioning you because I would not have been able to open it...

I @-mention jokats because I felt like he would be able to say yes or no the issue is in good standing.

I will refrain from using @- anyone form this point.

Sorry.

Julius, the API proposal is quite large. Maybe we can focus first on things that are not possible on top of the existing API?

For anything that is related to socket options, can you (instead of suggesting dedicated methods) suggest a SocketOptionName name, and provide the corresponding C optlevel and optname for Windows and Linux?

There are 3 parts in the API proposal, and I think we can look at them separately:

  • additional SocketOptionNames
  • convenience methods for working with socket options
  • additional NetworkInterface APIs

Julius, the API proposal is quite large. Maybe we can focus first on things that are not possible on top of the existing API?

For anything that is related to socket options, can you (instead of suggesting dedicated methods) suggest a SocketOptionName name, and provide the corresponding C optlevel and optname for Windows and Linux?

I have Tom, please let me know if I missed anywhere.

For features that are currently only possible via SetRawSocketOption, I think it's worth discussing proper APIs. While not an explicit blocker, it is strongly ideal that these new APIs would work cross-platform or at least not cause confusion when a user does want to support multiple platforms -- as an example, lack of confidence here is the reason the quick ack APIs were delayed.

For features that are currently available today, but you want to add some convenience methods for -- your Array additions, SocketExtensions, etc. -- I would suggest moving these into a 3rd party NuGet package. It looks like these will not significantly change peoples lives (e.g. turning a rare option from a 2-liner into a 1-liner), so I'd push back on adding them into BCL without some further feedback from more customers that these are pain points. If you'd still like these to be considered, I would move them into a separate issue so we can solicit that feedback and so that they don't block other things.

As @tmds pointed out, I would split this into multiple issues. I'd have one for e.g. ACK settings, one for NetworkInformation, and so on. This will help make things easier to digest by scoping discussion/brainpower, and also not cause one item to be blocked because another item is controversial etc.

As part of the motivation text, it might be helpful to note existing implementations in other stacks, or libraries. That can help demonstrate there's a need.

https://github.com/ngraziano/SharpRTSP

https://github.com/BogdanovKirill/RtspClientSharp

All are not RFC compliant without this unless they take the same measures I did @ https://github.com/juliusfriedman/net7mma_core (let alone secure)

http://www.camera-sdk.com/p_273-acknowledgements-onvif.html

And I will call out NAT as a problem there explicitly. That is where SendTo and ReceiveFrom are most useful IMHO on TCP, one liners or not, in a hot loop a stack frame is a stack frame and now you expecting people to write inlinable code or attempt to force such. This proposal is also partially about performance although you should not have to read in between the lines to see that.

@scalablecory is this a requirement to break these out and if so in which order would you prefer?

TBH i know it sounds petty but I can't get past here:

//Because it is truly right and just, this should be an intrinsic
public class Array{
   //Indicates if the array is null or empty (length == 0), outputs the length to len in the success case.
+ public static bool IsNullOrEmpty(Array array, out int len);
+ public static ToArray(params Object objects); //ToArray('',"",0)
+ public static ToArray<TElement>(params TElement elements); /// ToArray(1,2,3)
}

public class String{
   //Indicates if the string is null or empty (length == 0), outputs the length to len in the success case.
+ public static bool IsNullOrEmpty(String str, out int len);
+ public static bool IsNullOrEmptyOrWhitespace(String str, out int len);
}

Without these you throw away measured information which is not acceptable and a waste of the method call I have to make to obtain that information again. If you didn;t measure it and had some quantum way of telling if the array was empty or the same with the string then great teach me that please, (besides a *arr && memcmp on an empty array header with the type fields masked)

Can we focus on socket options for this issue? and move network interface related things to another issue?

These are the socket options in the proposal:

namespace System.Net.Sockets
{
  enum SocketOptionName
  {
    TcpMaximumRetransmission,
    TcpMaximumSegmentSize,
    NoSynRetries,
    Timestamp,
    TcpOffloadPreference,
    TcpCongestionAlgorithm,
    DelayFinAck,
    TcpNotUrgent,
    TcpStandardUrgency,
  }
}

Can you make a table with info for each option, like corresponding C optlevel and optname for Windows and Linux, and what 'type' of values the option takes?

For example:

| Name | Windows | Linux | value |
| --- | --- | --- | --- |
| TcpCongestionAlgorithm | ? | IPPROTO_TCP/TCP_CONGESTION | Linux: string name of congestion algorithm |

Its already in my source I had it there the first time, I'm sure you have much more access to headers and source than I do, would you mind doing the table work and I will make the issues separate? If we can compromise then what do you want left here..

Also an enum was not in my proposal for those options but whatever.

To be honest, I am getting lost at what this issue tracks. I wonder if it would be best to close it and start over with SIMPLE and CLEAR self-contained description of MINIMAL feature set and brief description of scenario. No links to external code unless it is also cleaned up and minimalistic.
If it is enabling larger end-to-end scenario, let's break it into small standalone chunks that make sense on their own. Let's NOT bring all of it in one issue.

Changes to 'Array' and 'string' should not be mixed in at all, they are currently just noise here and causing confusion.
Can we please try to start over? Maybe with first small thing, fine tuning the communication and information necessary to explain what and why is desired?

What is the ask here? I want everything listed and more but what I listed for sure.

We don't understand what you "want", so the ask is: Help us (or anyone else) to understand, WHAT you think is valuable addition to .NET and WHY.

The way to do it is by breaking what you want into small standalone chunks.
The same way, one would develop an application for a customer -- instead of "all or nothing" list of capabilities, you break down things into features (which you carefully describe in isolation from others as much as possible), you explain WHY for each (requirements, motivation), you even prioritize based on business needs.

Does that make sense?

Every single API I listed above I want, do you need a justification for each and every API in there? What do you want broken out the sections? Into issues or into parts? I already justified most of them in the summary or my above comments, Telnet ( and SSH I think) requires the URGENT parts, what else do you want?

Our process does not allow for us to have one issue that affects multiple areas (e.g. "networking" owns Socket and is separate from "System.Runtime" that owns Array). Additionally, packing a ton of disparate APIs -- even in a single area -- into one issue makes things much harder to discuss and reason about. As you've seen here, you're getting more feedback about how your issue is made, and not the APIs themselves.

Here's my guidance: create one issue for Array, one for String, one for NetworkInformation, one for new socket options. Skip all the convenience methods and focus only on core features needed to unblock you. If you absolutely want the convenience methods, split those out into separate issues so they don't get in the way of the core features. Be sure to include rationale in each issue.

@juliusfriedman multiple people have now suggested breaking things into multiple issues, and how. Please help us help you by doing so -- the feedback you're getting from everyone is "this is too much at once!". I'm going to close this issue now to help guide the process and discussion back on track. Please don't take this as discouragement -- I hope you can see that several people including myself are chiming in not to push back on the ask, but to help you push it forward optimally. I look forward to discussing your APIs with you in these new issues.

@scalablecory done.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

nalywa picture nalywa  路  3Comments

matty-hall picture matty-hall  路  3Comments

jchannon picture jchannon  路  3Comments

Timovzl picture Timovzl  路  3Comments

yahorsi picture yahorsi  路  3Comments