Skip to content

Socket support for TCP_QUICKACK #798

@felipepessoto

Description

@felipepessoto
Contributor

AB#1117049
System.Net.Sockets.Socket currently implements cross platform support for a large number of manually configurable socket options. We allow many TCP specific options, but do not support the standard TCP_QUICKACK option that is supported in both Windows and Unix.

Rationale and Usage

Nagles algorithm and TCP delayed ack are both used to solve similar problems around network congestion. Both are often enabled by default, but the interactions between them can cause extreme and unnecessary latency. For more information see this blog on MSDN, or this SD comment by John Nagle.

By enabling support for TCP_QUICKACK, we allow performance oriented developers to choose how to balance network congestion and latency.

The new option would be set via the standard SetSocketOption API. As the option is already supported natively on all supported platforms, the changes required would be very minimal.

Proposed API

namespace System.Net.Sockets
{
    // Defines socket option names for the <see cref='System.Net.Sockets.Socket'/> class.
    public enum SocketOptionName
    {
        #region SocketOptionLevel.Tcp
        TcpQuickAck = 12,
        #endregion
    }
}

[[API proposal added above by @rmkerr. Original issue below]]

Original Proposal

I'm not sure if Windows support this at socket level as Linux does. But delayed ack does not works well when the remote endpoint is using Nagle Algorithm (TCP_NODELAY = false).

Reference: https://blogs.technet.microsoft.com/nettracer/2013/01/05/tcp-delayed-ack-combined-with-nagle-algorithm-can-badly-impact-communication-performance/

Activity

karelz

karelz commented on May 25, 2018

@karelz
Member

I couldn't find existing option on Windows sockets APIs either.
@davidsh should we ping Windows folks to confirm?

cc @geoffkizer

davidsh

davidsh commented on May 25, 2018

@davidsh
Contributor

@davidsh should we ping Windows folks to confirm?

Yes @rmkerr could follow up with the usual TCP team that he already has worked with.

rmkerr

rmkerr commented on May 25, 2018

@rmkerr
Contributor

I'll reach out to the TCP team to confirm. In the meantime, I recommend checking out the documentation here on managing some of the issues with the Nagle algorithm and delayed ack.

felipepessoto

felipepessoto commented on May 25, 2018

@felipepessoto
ContributorAuthor

@rmkerr, I read that article, one of the issues I intend to prevent is the problem with Nagle algorithm

rmkerr

rmkerr commented on May 29, 2018

@rmkerr
Contributor

It looks like WinSock does support this! We can either set TCP_QUICKACK via setsocketopt or SIO_TCP_SET_ACK_FREQUENCY via WSAIoctl. In the interest of cross platform consistency we should probably use TCP_QUICKACK.

I think that this is definitely worth taking through API review, as it is a very useful tool for reducing latency when the Nagle algorithm is enabled.

rmkerr

rmkerr commented on May 29, 2018

@rmkerr
Contributor

System.Net.Sockets.Socket currently implements cross platform support for a large number of manually configurable socket options. We allow many TCP specific options, but do not support the standard TCP_QUICKACK option that is supported in both Windows and Unix.

Rationale and Usage

Nagles algorithm and TCP delayed ack are both used to solve similar problems around network congestion. Both are often enabled by default, but the interactions between them can cause extreme and unnecessary latency. For more information see this blog on MSDN, or this SD comment by John Nagle.

By enabling support for TCP_QUICKACK, we allow performance oriented developers to choose how to balance network congestion and latency.

The new option would be set via the standard SetSocketOption API. As the option is already supported natively on all supported platforms, the changes required would be very minimal.

Proposed API

namespace System.Net.Sockets
{
    // Defines socket option names for the <see cref='System.Net.Sockets.Socket'/> class.
    public enum SocketOptionName
    {
        #region SocketOptionLevel.Tcp
        TcpQuickAck = 12,
        #endregion
    }
}
karelz

karelz commented on May 29, 2018

@karelz
Member

Should be fairly straightforward - model it by other options
We need to make sure the TCP_QUICKACK is part of public Windows headers (or we need to wait for public MSDN docs). The number (12) should match the value in them (on Windows).

felipepessoto

felipepessoto commented on May 31, 2018

@felipepessoto
ContributorAuthor

@rmkerr, These TCP constant options are the same in all SO? http://www.cse.scu.edu/~dclark/am_256_graph_theory/linux_2_6_stack/linux_2tcp_8h-source.html.
How did you find the constant 12 in Windows?

rmkerr

rmkerr commented on May 31, 2018

@rmkerr
Contributor

This issue actually needs to be updated -- the API is slightly different than expected on Windows. Rather than using the standard setsocketopt interface, Windows allows for more granular control over ack frequency via the WSAIoctl control code SIO_TCP_SET_ACK_FREQUENCY. That means that we can add the ability to disable delayed ack on all platforms, but the implementation may need to be platform specific. I think the implementation details need more discussion. Depending on the results of that discussion we may need to go through API review again.

alfredmyers

alfredmyers commented on Jun 2, 2018

@alfredmyers
Contributor

While researching on the topic, I found an issue over on the Kestrel repo.

The folks over there had an issue about it but it was closed aspnet/KestrelHttpServer#480

juliusfriedman

juliusfriedman commented on Jun 6, 2018

@juliusfriedman
Contributor

I have some methods related to this however by other names, e.g

I believe the name "CongestionAlgorithm" is appropriate and the api allows a broader range of options:

      #region CongestionAlgorithm

        const int TcpCongestionAlgorithm = 12;

        static readonly System.Net.Sockets.SocketOptionName TcpCongestionAlgorithmOption = (System.Net.Sockets.SocketOptionName)TcpCongestionAlgorithm;

        public static void SetTcpCongestionAlgorithm(System.Net.Sockets.Socket socket, int value = 1)
        {
            SetTcpOption(socket, TcpCongestionAlgorithmOption, value);
        }

        public static void DisableTcpCongestionAlgorithm(System.Net.Sockets.Socket socket)
        {
            SetTcpCongestionAlgorithm(socket, 0);
        }

        public static void EnableTcpCongestionAlgorithm(System.Net.Sockets.Socket socket, int algorithmType = 1)
        {
            SetTcpCongestionAlgorithm(socket, algorithmType);
        }

        #endregion

Also for Retransmission:

    #region Retransmission

        static System.Net.Sockets.SocketOptionName TcpMaximumRetransmissionOptionName = (System.Net.Sockets.SocketOptionName)(Common.Extensions.OperatingSystemExtensions.IsWindows ? 5 : 18);

        [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
        public static void SetMaximumTcpRetransmissionTime(System.Net.Sockets.Socket socket, int amountInSeconds = 3)
        {
            //On windows this is TCP_MAXRT elsewhere USER_TIMEOUT

            //Mono checks the options and will not call setsocketopt if the name is not known to the Mono Runtime.
            //A work around would be to either define the call for get and set socketopt in this library or call the SetSocketOption_internal method for mono.
            SetTcpOption(socket, TcpMaximumRetransmissionOptionName, amountInSeconds);
        }

        [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
        public static void DisableTcpRetransmissions(System.Net.Sockets.Socket socket)
        {
            SetMaximumTcpRetransmissionTime(socket, 0);
        }

        [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
        public static void EnableTcpRetransmissions(System.Net.Sockets.Socket socket)
        {
            SetMaximumTcpRetransmissionTime(socket);
        }

        public static int GetMaximumTcpRetransmissionTime(System.Net.Sockets.Socket socket)
        {
            int len = Common.Binary.BytesPerInteger;

            //Todo, static local allocation
            byte[] value = socket.GetSocketOption(System.Net.Sockets.SocketOptionLevel.Tcp, TcpMaximumRetransmissionOptionName, len);

            if (Common.Extensions.Array.ArrayExtensions.IsNullOrEmpty(value, out len) || len < Common.Binary.BytesPerInteger) return -1;

            return Common.Binary.Read32(value, Common.Binary.BytesPerInteger, BitConverter.IsLittleEndian);

        }

        #endregion

And for others (most already with baked in checked for OS) see(https://github.com/juliusfriedman/net7mma/blob/master/Common/Extensions/SocketExtensions.cs):

NoSynRetries, Timestamp, OffloadPreference, DelayFinAck, GetMaximumTransmittableUnit, Urgency and last but not least MaximumSegmentSize.

stephentoub

stephentoub commented on Nov 23, 2018

@stephentoub
Member

Depending on the results of that discussion we may need to go through API review again.

Removing api-approved until this is resolved.

transferred this issue fromdotnet/corefxon Dec 12, 2019

24 remaining items

added this to the Future milestone on Jul 17, 2020
karelz

karelz commented on Jul 17, 2020

@karelz
Member

Moving to Future based on above analysis by @antonfirsov: #798 (comment)

ericsampson

ericsampson commented on Nov 1, 2020

@ericsampson

This would be great to see in some form in .NET 6

antonfirsov

antonfirsov commented on Nov 1, 2020

@antonfirsov
Member

It's possible to configure this on .NET 5 on Linux and Windows:

Linux (per call):

socket.SetRawSocketOption((int) SocketOptionLevel.Tcp, 12, BitConverter.GetBytes(1));

Windows (once):

socket.IOControl(SIO_TCP_SET_ACK_FREQUENCY, BitConverter.GetBytes(1), Dummy);

I can't see a good cross-platform abstraction for the option though. @ericsampson if you have a real-life use-case for this, is there any problem with the platform-specific solutions I'm suggesting?

added a commit that references this issue on Jul 7, 2022
wfurt

wfurt commented on Nov 30, 2022

@wfurt
Member

triage: workaround exist through platform specific. Since there is no consistent cross-platform it seems like bad fit for general Socket API. We can revisit this in the future.

ghost locked as resolved and limited conversation to collaborators on Dec 30, 2022
modified the milestones: Future, 8.0.0 on Mar 22, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @ericsampson@felipepessoto@stephentoub@rmkerr@scalablecory

        Issue actions

          Socket support for TCP_QUICKACK · Issue #798 · dotnet/runtime