1
votes

I am trying to find a way for my application to detect disconnects via TCP when I am connecting to a remote server. I was thinking the obvious way to do this was to use the TCP keepalive option. This is an MFC C++ application and the MySock class inherits from CSocket. I have tried setting the SO_KEEPALIVE option before and after the call to Connect and I have never seen a keepalive packet get sent out via wireshark. I can see all the data I am sending on the connection fine so I know it is connected and working.

if (!(m_connection = new MySock(this)))
{
    AfxMessageBox ("Failed to allocate socket! Close and restart app.");
    m_connect.EnableWindow(true);
    return false;
}

if (!m_connection->Create())
{
    AfxMessageBox ("Failed to create client socket! Close and restart app.");
    m_connect.EnableWindow(true);
    return false;
}

TCHAR buff[128];
DWORD optval = true;
int temp = sizeof(DWORD);

if (!m_connection->GetSockOpt(SO_KEEPALIVE, (void*)&optval, &temp, SOL_SOCKET))
{
    wsprintf (buff, "GetSockOpt Error %d", GetLastError());
    AfxMessageBox (buff);
}

wsprintf (buff, "GetSockOpt %d", optval);
AfxMessageBox (buff);

optval = true;
temp = sizeof(DWORD);
if (!m_connection->SetSockOpt(SO_KEEPALIVE, (void*)&optval, temp, SOL_SOCKET))
{
    wsprintf (buff, "SetSockOpt Error %d", GetLastError());
    AfxMessageBox (buff);
}

optval = false;
temp = sizeof(DWORD);
if (!m_connection->GetSockOpt(SO_KEEPALIVE, (void*)&optval, &temp, SOL_SOCKET))
{
    wsprintf (buff, "GetSockOpt Error %d", GetLastError());
    AfxMessageBox (buff);
}

wsprintf (buff, "GetSockOpt %d", optval);
AfxMessageBox (buff);

CString consoleText;

m_console.GetWindowText(consoleText);

consoleText += "Control Connected!\r\n";

m_console.SetWindowText(consoleText);

if (!m_connection->Connect(address, CONTROL_PORT))
{
    AfxMessageBox ("Failed to connect");
    m_connection->Close();
    delete m_connection;
    m_connection = NULL;
    m_connect.EnableWindow(true);
    return false;
}

optval = false;
temp = sizeof(DWORD);
if (!m_xcmp->GetSockOpt(SO_KEEPALIVE, (void*)&optval, &temp, SOL_SOCKET))
{
    wsprintf (buff, "GetSockOpt Error %d", GetLastError());
    AfxMessageBox (buff);
}
wsprintf (buff, "GetSockOpt %d", optval);
AfxMessageBox (buff);

The value of optval when calling GetSockOpt before calling SetSockOpt is 0 and after calling it is 1. Also the value after connecting is still set to 1. I set optval to 0 before calling GetSockOpt just to be sure nothing weird was happening. None of the Get or Set are returning errors. Any help or ideas would be appreciated. My current theory is that client side connections (I am connecting to a Listening socket) do not send the keepalives. Do keepalives only come from a connection that was the listener? Thanks for your time.

2

2 Answers

1
votes

The default TCP keep-alive timeout is 7200 seconds, i.e. two hours, so you have to wait that much to see any of it in action. This timeout is usually controlled with sysctl(8) on Unix, Windows probably has some registry key under KEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters for this, I have no idea. Here's a brief overview of the TCP KeepAlive mechanism.

Let me also note that this is not terribly useful. You'd be much better off implementing some sort of a heartbeat mechanism in your application protocol to detect not only disconnected, but also lagging and stuck peers.

1
votes

setsockopt() can only enables/disables 'keep-alive' option, and the default TCP keep-alive timeout is 7200 seconds. Have a look at WSAIoctl() | SIO_KEEPALIVE_VALS, it can also change the keep-alive timeout and interval.