locked
TCP keepAlive settings problem

    Question

  • am trying to set the keepalive timer by the setting the keep alive option in setsockopt

    Cli.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);

    i found something  in .net similar to the WSAIoctl(socketid,SIO_KEEPALIVE_VALS,struct tcp_keepalive settings,...) in winsock2 library which sends keep alive messages every n milliseconds specified by the tcp_keepalive.keepalivetime.

    the api am trying to use is this

    Cli.IOControl(IOControlCode.KeepAliveValues,...,...);

    can someone tell me how to use this api to set the keepalive time and interval to the socket or does the .NET framework is incapable of doing something similar.

     

    Thanks in advance

    Sathish

     

     

    Thursday, February 01, 2007 3:50 PM

Answers

  •  

    If you need to use TCP keepalive options to detect network failure, you must turn it on for all ends that requires the message.

    You can turn on keepalive from c#, but the TCP-defaults implemented in .Net (from rfc) are to send something like a packet every 10 seconds starting after 2 hours.

    The mechanisms are almost exactly the same as sending your own message, only difference is the packets contains minimum of data - return contains only header, so it might be chopped away (not routed) by some routers/firewalls. In most projects I tend to end up writing a ping mechanism of my own because the other ends need to know a network fault at the same time (ping message not received within a given interval means down). Writing a mechanism of your own can also give you a quicker ways of detecting it or you have to change send timeouts etc to match your specs.

    Anyways, here is the method I use to enable keepalive on sockets:

    // "consts" to help understand calculations
    const int bytesperlong = 4; // 32 / 8
    const int bitsperbyte = 8;

     private bool SetKeepAlive(Socket sock, ulong time, ulong interval)
     {
                try
                {
                    // resulting structure
                    byte[] SIO_KEEPALIVE_VALS = new byte[3 * bytesperlong];

                    // array to hold input values
                    ulong[] input = new ulong[3];

                    // put input arguments in input array
                    if (time == 0 || interval == 0) // enable disable keep-alive
                        input[0] = (0UL); // off
                    else
                        input[0] = (1UL); // on

                    input[1] = (time); // time millis
                    input[2] = (interval); // interval millis

                    // pack input into byte struct
                    for (int i = 0; i < input.Length; i++)
                    {
                        SIO_KEEPALIVE_VALS[i * bytesperlong + 3] = (byte)(input[ i ] >> ((bytesperlong - 1) * bitsperbyte) & 0xff);
                        SIO_KEEPALIVE_VALS[i * bytesperlong + 2] = (byte)(input[ i ] >> ((bytesperlong - 2) * bitsperbyte) & 0xff);
                        SIO_KEEPALIVE_VALS[i * bytesperlong + 1] = (byte)(input[ i ] >> ((bytesperlong - 3) * bitsperbyte) & 0xff);
                        SIO_KEEPALIVE_VALS[i * bytesperlong + 0] = (byte)(input[ i ] >> ((bytesperlong - 4) * bitsperbyte) & 0xff);
                    }
                    // create bytestruct for result (bytes pending on server socket)
                    byte[] result = BitConverter.GetBytes(0);
                    // write SIO_VALS to Socket IOControl
                    sock.IOControl(IOControlCode.KeepAliveValues, SIO_KEEPALIVE_VALS, result);
                }
                catch (Exception)
                {
                    return false;
                }
                return true;
            }

    Friday, February 02, 2007 4:00 PM

All replies

  • Can you explain what do you want to do? In .Net Server sockets are alive untill they are stopped and client sockets are alive untill they are disconnected. So whether they are sending/receiving data or not, they are alive. I think you need this right? If so just leave it and let it handled by Managed Sockets.

    Note: The above explaination is true for System.Net.Sockets.Socket and its helper classes System.Net.Sockets.TcpListener and System.Net.Sockets.TcpClient.

    Best Regards,

    Rizwan aka RizwanSharp

    • Proposed as answer by defza Friday, June 01, 2012 3:31 PM
    Thursday, February 01, 2007 5:08 PM
  • let us assume that the network cable is unplugged if we are sending or receiving data through that socket.the sender will get 10054(Connection reset) socket exception but the receiver won't be able to receive this exception and so it assumes that the sender is alive...

    so we tried to send a health or keepalive packet for every n seconds to find the broken pipe or dead socket.

    Latest versions of Windows have built in support for keepalive using TCPKeepAlive...the default keepalive values are taken from registry.Is there any way we can change the keepalive timeinterval using C#.net.(because the default value for keepalive is 2 hrs)

     

     

    Friday, February 02, 2007 6:36 AM
  • On the receiving end while a application is in call to NetworkStream.Read or Socket.Receive method or their equivalent Asynchronous methods, whenever the sending socket is disconnected you get 0 bytes from Read or Receive methods.

    e.g:

    int readBytes = networkStream.Read(...........);

    if(readBytes == 0)

    {

            // Disconnected..........

    }

    do the same with Socket.Receive, EndRead, EndReceived whatever you are using.

    You dont need to send keep alive messages manually, the above mentioned technique will do the work for you.

    Best Regards,

    Rizwan aka RizwanSharp

    Friday, February 02, 2007 2:58 PM
  •  

    If you need to use TCP keepalive options to detect network failure, you must turn it on for all ends that requires the message.

    You can turn on keepalive from c#, but the TCP-defaults implemented in .Net (from rfc) are to send something like a packet every 10 seconds starting after 2 hours.

    The mechanisms are almost exactly the same as sending your own message, only difference is the packets contains minimum of data - return contains only header, so it might be chopped away (not routed) by some routers/firewalls. In most projects I tend to end up writing a ping mechanism of my own because the other ends need to know a network fault at the same time (ping message not received within a given interval means down). Writing a mechanism of your own can also give you a quicker ways of detecting it or you have to change send timeouts etc to match your specs.

    Anyways, here is the method I use to enable keepalive on sockets:

    // "consts" to help understand calculations
    const int bytesperlong = 4; // 32 / 8
    const int bitsperbyte = 8;

     private bool SetKeepAlive(Socket sock, ulong time, ulong interval)
     {
                try
                {
                    // resulting structure
                    byte[] SIO_KEEPALIVE_VALS = new byte[3 * bytesperlong];

                    // array to hold input values
                    ulong[] input = new ulong[3];

                    // put input arguments in input array
                    if (time == 0 || interval == 0) // enable disable keep-alive
                        input[0] = (0UL); // off
                    else
                        input[0] = (1UL); // on

                    input[1] = (time); // time millis
                    input[2] = (interval); // interval millis

                    // pack input into byte struct
                    for (int i = 0; i < input.Length; i++)
                    {
                        SIO_KEEPALIVE_VALS[i * bytesperlong + 3] = (byte)(input[ i ] >> ((bytesperlong - 1) * bitsperbyte) & 0xff);
                        SIO_KEEPALIVE_VALS[i * bytesperlong + 2] = (byte)(input[ i ] >> ((bytesperlong - 2) * bitsperbyte) & 0xff);
                        SIO_KEEPALIVE_VALS[i * bytesperlong + 1] = (byte)(input[ i ] >> ((bytesperlong - 3) * bitsperbyte) & 0xff);
                        SIO_KEEPALIVE_VALS[i * bytesperlong + 0] = (byte)(input[ i ] >> ((bytesperlong - 4) * bitsperbyte) & 0xff);
                    }
                    // create bytestruct for result (bytes pending on server socket)
                    byte[] result = BitConverter.GetBytes(0);
                    // write SIO_VALS to Socket IOControl
                    sock.IOControl(IOControlCode.KeepAliveValues, SIO_KEEPALIVE_VALS, result);
                }
                catch (Exception)
                {
                    return false;
                }
                return true;
            }

    Friday, February 02, 2007 4:00 PM
  • thanks for ur quick reply..

    i implemented the same logic with C# and its working correctly.. now i'm sending the keepalive packet for every 30secs(time millis) and for 1sec(time interval)... it is working correctly and if i'm disconnectiong the wire i'm also getting socket exception(10054) with the TCP KeepaLive feature...so i'm able to find the broken pipe...

    if i'm disconnecting the wire the sender sends the tcp keepalive for every 30 secs and if the sender havn't received any reply with in the time interval(1 sec) means it is transmitting the tcp keepalive for n times i.e for n secs...

    now i want to change this number n i.e no of times the tcpkeepalive packet is to be retransmitted before declaring it as dead socket...

    is there any possible way of changing the no of times retry parameter through my program in C#

     

    Friday, February 02, 2007 9:38 PM
  • i want to find broken connection of tcp not the smooth close of tcp..

    u can try this exception by

    create a server that accepts the socket and put the socket in a receive mode...

    create a client socket and connect with the server and make the socket sleep for some seconds and just unplug the network cable..u will get exception in client socket(10054)

    but in the server socket u wont get 0 bytes or socket exception so for  identifying this broken pipe i'm using TCP KeepAlive feature..

     

    Friday, February 02, 2007 9:43 PM
  • Ah... Sorry I was a bit confused as to what the real problem was. I haven't had the need to change this from C# (or C++) yet, but I had to "tweak" something similar for some SSH connections - to make XP more agressively keeping the connections open earlier through registry... There are parameters like:

    HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\TcpMaxDataRetransmissions

    Some info and lists of parameters you can set using registry for XP can be found in MSKB arcticle 314053

    Please post code or hints if you solve it!


     

    Sunday, February 04, 2007 10:54 PM
  • Hi there!
     
    This looks like it'll solve my hanging connection problems as well.
     
    Problem is, I have a multithreading asynchronous socket server, and here's the class (in VB.NET, sorry):
     
     
     
    Imports System.Net
    Imports System.Net.Sockets
    Imports System.Text
     
    Public Class Client : Inherits User
        Implements IDisposable
     
        'Constants
        Const READ_BUFFER_SIZE As Int32 = 100
        Const LINE_SEPERATOR As Char = Chr(4)
        Const REC_SEPERATOR As Char = Chr(30)
     
        'Private variables
        Private mSock As Socket                                         'Connection to the client
        Private mBuffer(READ_BUFFER_SIZE) As Byte                       'Buffer for received data
        Private readString As New StringBuilder(READ_BUFFER_SIZE, 500)
     
        'Events
        Public Event LineReceived(ByVal activeClient As Client, ByVal message As String)
        Public Event ErrorReceived(ByVal activeClient As Client, ByVal message As String)
     
        Public ReadOnly Property Sock() As Socket
            Get
                Return mSock
            End Get
        End Property
     
        Public Sub New(ByVal sock As Socket, ByVal signedOn As DateTime)
            MyBase.New()
     
            mSock = sock
            Me.SignedOn = signedOn
     
            Dim receiveData As New AsyncCallback(AddressOf OnReceivedData)
            mSock.BeginReceive(mBuffer, 0, mBuffer.Length, SocketFlags.None, receiveData, Me)
        End Sub
     
        Public Sub OnReceivedData(ByVal ar As IAsyncResult)
            Try
                Dim content As String
                Dim intEOFPos As Int32
                Dim bytesRead As Int32 = mSock.EndReceive(ar)
     
                If (bytesRead > 0) Then
                    'There might be more data
                    'Store the data received so far
                    readString.Append(Encoding.ASCII.GetString(mBuffer, 0, bytesRead))
     
                    'Check for EOF tag
                    content = readString.ToString()
                    intEOFPos = content.IndexOf(LINE_SEPERATOR)
     
                    Do
                        intEOFPos = content.IndexOf(LINE_SEPERATOR)
                        If (intEOFPos > -1) Then
                            'If EOF detected, call LineReceived function for processing
                            RaiseEvent LineReceived(Me, content.Substring(0, intEOFPos))
     
                            'Delete readString
                            readString.Remove(0, intEOFPos + 1)
     
                            'Reset the message and find new intEOFPos
                            content = readString.ToString()
                            intEOFPos = content.IndexOf(LINE_SEPERATOR)
                        End If
                    Loop Until intEOFPos = -1
     
                    Dim receiveData As New AsyncCallback(AddressOf OnReceivedData)
                    mSock.BeginReceive(mBuffer, 0, mBuffer.Length, SocketFlags.None, receiveData, Me)
                Else
                    RaiseEvent ErrorReceived(Me, String.Format("Client {0}, disconnected at {1}", mSock.RemoteEndPoint, DateTime.Now()))
                End If
                'Catch ex As System.InvalidOperationException
                'Don't worry about it
            Catch ex As Exception
                RaiseEvent ErrorReceived(Me, String.Format("Client {0}, disconnected  at {1}- Reason: {2}", mSock.RemoteEndPoint, DateTime.Now(), ex.Message))
            End Try
        End Sub
     
        Public Overloads Sub Dispose() Implements IDisposable.Dispose
            GC.SuppressFinalize(Me)
        End Sub
    End Class
     
    The ErrorReceived class takes care of cleanup, shutting down and disconnecting the socket.
     
    Now, how would I implement your KeepAlive function you posted above? So it's handled for every client connected? Do I run a maintenance thread that goes through all my clients that are connected (they're stored in a dictionary generic object), and sends them this? And if an exception is thrown, call the appropriate cleanup method?
     
    Monday, February 05, 2007 5:50 AM
  • byte[] inValue = new byte[] { 1, 0, 0, 0,48,117,0 ,0,1 ,0 ,0 ,0 };// 1-->For Enabling TCPKeepAlive,30 secs interval,1 sec duration after sending tcpkeepalive

    byte[] outvalue=new byte[10];

    After accepting the connection a socket object is returned u should set the parameters to the accepted socket...

    Socket Client=ServerSocket.accept();

    Client.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.KeepAlive, 1);//for enabling tcpkeepalive

    Client.IOControl(IOControlCode.KeepAliveValues, inValue, outvalue);

    this is what i'm using ...check by unplugging the client network cable with in 30-40 secs u will get socket exception(10054) on ur socket that was used to connect to the client...

     

    Monday, February 05, 2007 6:23 AM
  • thanks for ur help...

    i don't want to make this work by changing the registry..

    i want this to be done only through the code and i'm trying on it....

    i think there might be way we can achieve this through C#

    Monday, February 05, 2007 6:33 AM
  • Okay!
     
    This is what I did on the OnConnectRequest callback:
     

    newClient.Sock.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.KeepAlive, 1)

    newClient.Sock.IOControl(IOControlCode.KeepAliveValues, inValue, outValue)

    And I declared inValue and outValue as private global variables. So basically I keep the exception handling on the Client level as I have before, and the KeepAlive functionality will trigger those exceptions?
     
     
    Monday, February 05, 2007 6:40 PM
  • Hello satishraj
    After adding

    byte[] inValue = new byte[] { 1, 0, 0, 0,48,117,0 ,0,1 ,0 ,0 ,0 };// 1-->For Enabling TCPKeepAlive,30 secs interval,1 sec duration after sending tcpkeepalive

    byte[] outvalue=new byte[10];


    Socket Client=ServerSocket.accept();

    Client.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.KeepAlive, 1);//for enabling tcpkeepalive

    Client.IOControl(IOControlCode.KeepAliveValues, inValue, outvalue);

    it will work fine but i want to set inValue on user level. means i want set like

    int interval = 2;

    int keepalivepakettime = 40;

    then it will generate inValue automatically.

    byte[] inValue = new byte[] { ---- interval + keepalivepakettime ----- };


    thanks in advance.

    Wednesday, March 07, 2007 1:51 PM
  • Setting the IOControl is easy... I would show you that... but it doesn't work.

     

    I can get "client" sockets to do keepAlive:  easy as... RTFM and it works first time.  WireShark traces show the keepAlive packet reliably being sent from the client to the listener side of the connection.

     

    But I can't get accepted sockets on the listener to send keepAlive... which is what you describe above.  I need both sides to be able to detect dead connections... so keepAlive(detectDead actually) from both directions is important.

     

    The correct bits are being set:  you can read back the settings and they are "correct".  It's just that WireShark traces prove keepAlive isn't actually being sent.

     

    Has anyone actually managed to get the listener side of a socket connection to send keepAlive packets?

    Monday, April 16, 2007 11:53 AM
  •  

    excellent post.

     

    There is loads of rubbish talked about Keep alive but this post nailed it.

     

    Here is a VB version of the routine

     

    ' "consts" to help understand calculations

    Const bytesperlong As Int32 = 4 ' // 32 / 8

    Const bitsperbyte As Int32 = 8

    Private Function SetKeepAlive(ByVal sock As Socket, ByVal time As ULong, ByVal interval As ULong) As Boolean

    Try

    ' resulting structure

    Dim SIO_KEEPALIVE_VALS((3 * bytesperlong) - 1) As Byte

    ' array to hold input values

    Dim input(2) As ULong

    ' put input arguments in input array

    If (time = 0 Or interval = 0) Then ' enable disable keep-alive

    input(0) = CType(0, ULong) ' off

    Else

    input(0) = CType(1, ULong) ' on

    End If

    input(1) = (time) ' time millis

    input(2) = (interval) ' interval millis

    ' pack input into byte struct

    For i As Int32 = 0 To input.Length - 1

    SIO_KEEPALIVE_VALS(i * bytesperlong + 3) = CByte((CLng(input(i) >> ((bytesperlong - 1) * bitsperbyte)) And &HFF))

    SIO_KEEPALIVE_VALS(i * bytesperlong + 2) = CByte((CLng(input(i) >> ((bytesperlong - 2) * bitsperbyte)) And &HFF))

    SIO_KEEPALIVE_VALS(i * bytesperlong + 1) = CByte((CLng(input(i) >> ((bytesperlong - 3) * bitsperbyte)) And &HFF))

    SIO_KEEPALIVE_VALS(i * bytesperlong + 0) = CByte((CLng(input(i) >> ((bytesperlong - 4) * bitsperbyte)) And &HFF))

    Next

    ' create bytestruct for result (bytes pending on server socket)

    Dim result() As Byte = BitConverter.GetBytes(0)

    ' write SIO_VALS to Socket IOControl

    sock.IOControl(IOControlCode.KeepAliveValues, SIO_KEEPALIVE_VALS, result) '

    Catch es As Exception

    Return False

    End Try

    Return True

    End Function

    • Proposed as answer by RELSC Tuesday, October 13, 2009 8:10 PM
    Thursday, March 20, 2008 10:19 PM
  •  

    Thanks. Its working perfectly. thanks for ur coding.

     

    Karthik.P

    Dhanus Technologies
    Thursday, May 08, 2008 7:23 AM
  • Hi everybody. I'm trying to make a tunnel for a RDP connection using tcp stream sockets. Till now i've achieved that when one side closes the connection intentionally, evrything works OK. I've used your example here to make the sockets KeepAlive sensible. The result is that the only sensible machine is the one whose connection drops down - i'll explain: one PC is the server, another one is the client; I'm unplugging the cable of the server - the only machine that detects the problem is the server, the client goes on. If you unplug the clien cable the story goes reversally - the server doesn't detect it and the client yes. Am I doing somthing wrong?? Any suggestions? Thank You in advance!
    Friday, April 17, 2009 9:33 AM
  • I have set up TCP Keep Alives on my socket.  I am using NetMonitor and can see a keep alive flag being sent every 30 seconds and an acknowledgement being received.

    If I pull out a cable I see the keep alive flags being sent every 10 seconds. (This happens 5 time which is the default for TcpMaxDataRetransmissions) and then stops.

    The 30 second and 10 second values were set using IOControl after the socket was connected.

    I expected an event to fire on my socket (or something like that) so that it knows the connection has been broken.  How are you receiving the 100054 error?

    I am half expecting you to say that you have to send something to get the error but that makes me ask what is the point of TCP keep alives.

     

     

     

     

    Monday, March 07, 2011 5:02 PM
  • "and an acknowledgement being received."  Unless I misunderstand that mean that the connection is not broken...  That's the peer responding???
    http://www.alanjmcf.me.uk/ Please follow-up in the newsgroup. If I help, mark the question answered
    Thursday, March 10, 2011 10:10 PM
  • Yes, an acknowledgement is sent back from the other end of the connection.  My application however is not aware of this (i.e. there is no receive going on) as it is done at the tcp keep alive level not the application code.
    Monday, March 14, 2011 2:13 PM
  • @Ted Carron: Your code appears to work perfectly. However, I did try to tidy it a bit to improve concision and readability:

     Private Shared Function SetKeepAlive(ByRef tcpSocket As Socket, ByVal keepAliveTime As UInteger, ByVal keepAliveInterval As UInteger) As Boolean
      ' Pack three params into 12-element byte array; not sure about endian issues on non-Intel
      Dim SIO_KEEPALIVE_VALS(11) As Byte
      Dim keepAliveEnable As UInteger = 1
      If (keepAliveTime = 0 Or keepAliveInterval = 0) Then keepAliveEnable = 0
      ' Bytes 00-03 are 'enable' where '1' is true, '0' is false
      ' Bytes 04-07 are 'time' in milliseconds
      ' Bytes 08-12 are 'interval' in milliseconds
      Array.Copy(BitConverter.GetBytes(keepAliveEnable),  0, SIO_KEEPALIVE_VALS, 0, 4)
      Array.Copy(BitConverter.GetBytes(keepAliveTime),   0, SIO_KEEPALIVE_VALS, 4, 4)
      Array.Copy(BitConverter.GetBytes(keepAliveInterval), 0, SIO_KEEPALIVE_VALS, 8, 4)
    
      Try
       Dim result() As Byte = BitConverter.GetBytes(CUInt(0)) ' Result needs 4-element byte array?
       tcpSocket.IOControl(IOControlCode.KeepAliveValues, SIO_KEEPALIVE_VALS, result)
      Catch e As Exception
       Return False
      End Try
      Return True
     End Function
    

    I have verified equivalency with yours with Console.Write(BitConverter.ToString(SIO_KEEPALIVE_VALS)) on a few 32-bit test values.

    (Sorry for the necropost, but there are several outside links to this thread; I hope to meaningly contribute for future viewers.)




    • Edited by -This Guy- Sunday, June 19, 2011 3:57 AM Now I just hate this site's editor
    Sunday, June 19, 2011 3:49 AM
  • This Guy,

    How can i implement that function on a tcp Client?

    I am new in TCP Socket programming!

    Thanks.

    LE: In the meantime i have discovered how to implement it

    In the Form_load sub i have used like this:

             tcpSocket = New Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)
                tcpSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, True)
                SetKeepAlive(tcpSocket, keepalivetime, keepaliveinterval)

                Try
                    tcpSocket.Connect(ipEndPoint)
                Catch e As SocketException
                    ...
                End Try

    • Edited by operagust Wednesday, April 25, 2012 9:55 AM
    Monday, April 23, 2012 2:24 PM
  • Hi Lars,

    The solution you proposed to appears to be resolving one of the issue being encountered by my application. But I need more clarity on how to implement it.

    One of my C# application is hosted on multiple servers and the request made by user is routed through Load balancer to the servers. When the request is completed, server responds back to browser using reverse proxy (Time out limit 5 minutes). Now the issue is that very often when the request takes more than 5 minutes, the connection between reverse proxy and the web server is reset, thus users end up receiving 502 proxy error.

    Please can you let me know if the implementation of the above code will resolve the issue?

    Thank you.

    Thursday, May 17, 2012 8:10 AM