Search code examples
c#tcpclientkeep-alive

No KeepAlive requests after setting it for SocketOptionLevel.Socket


I have and two applications The 1st one opens the TcpClient:

 client = new TcpClient();
 client.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);
 client.NoDelay = true;
 client.Connect(settings.Hostname, settings.Port);

The 2nd one listens:

        var listener = new TcpListener(settings.IPAddress, settings.Port);

        var listenerThread = new Thread(() => Listen(listener, cancellationTokenSource.Token))
        {
            IsBackground = true,
            Priority = ThreadPriority.AboveNormal,
        };

       private void Listen(TcpListener tcpListener, CancellationToken cancellationToken)
       {
        tcpListener.Start();

        // stop listener on cancellation
        cancellationToken.Register(tcpListener.Stop);

        try
        {
            while (!cancellationToken.IsCancellationRequested)
            {
                var client = tcpListener.AcceptTcpClient();
                client.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);

                // Register client in background
                Task.Run(() => RegisterClientConnection(client), cancellationToken)
                    .LogErrors(SystemLogManager.Current, "Error while registering client connection");
            }
        }
        catch (SocketException)
        {
            // logging
        }
    }

And some logic to read the stream:

using (var clientStream = tcpClient.GetStream())
...
bytesRead = await clientStream.ReadAsync(messageBuffer, messageBufferPointer, tcpClient.ReceiveBufferSize, cancellationToken);

The strange part that I do not see any keep alive requests in the wireshark and the connection is broken after 5 minutes by azure firewall. Why is keep alive not working in my case?

both apps on .netframework 4.7.2


Solution

  • By default, TCP keepalives are sent every 2 hours, so that's why they're not showing up in less than 5 mintues.

    If you have Win 10 (1709) or newer, you can adjust this timing by setting the TCP_KEEPIDLE and TCP_KEEPINTVL socket options; a more universal solution (Windows 2000+) is to send the socket handle a SIO_KEEPALIVE_VALS IOCTL.

    However, note that TCP keepalives are an optional part of the TCP specification. It is possible that routers and software networking may just ignore TCP keepalives. Windows supports TCP keepalives, but I'm not sure if the Azure components you're using support TCP keepalives or not.

    Since you control both sides of the software, I recommend creating a keepalive message at the application protocol level (as described on my blog). This is relatively easily done by sending an empty message; e.g., if you're using length-prefix message framing, then just send a length prefix of 0. Using an application-level keepalive forces all intermediate routers to deliver it (since it's application data), and is just as easy (if not easier) to implement than a TCP-level keepalive.