Windows 下TCP長連接保持連接狀態TCP keepalive設置


TCP長連接建立完成后,我們通常需要檢測網絡的連接狀態,以反饋給客戶做響應的處理。通過設置TCP keepalive的屬性,打開socket的keepalive屬性,並設置發送底層心跳包的時間間隔。TCP/IP五層網絡模型,我們調用socket等接口是應用層的函數,TCP keepalive是在底層定時發送心跳報文,服務器端接收到底層的心跳報文直接丟棄,不關心其內容。

以下是windows下TCP keepalive設置的函數:

#include <mstcpip.h>
int CClientCtrl::socket_tcp_alive(int socket)
{
    int ret = 0;


    int keep_alive = 1;

    ret = setsockopt(socket, SOL_SOCKET, SO_KEEPALIVE, (char*)&keep_alive, sizeof(keep_alive));


    if (ret == SOCKET_ERROR)

    {

        printf("setsockopt failed: %d \n", WSAGetLastError());

        return -1;

    }


    struct tcp_keepalive in_keep_alive = {0};

    unsigned long ul_in_len = sizeof(struct tcp_keepalive);

    struct tcp_keepalive out_keep_alive = {0};

    unsigned long ul_out_len = sizeof(struct tcp_keepalive);

    unsigned long ul_bytes_return = 0;


    in_keep_alive.onoff = 1; /*打開keepalive*/

    in_keep_alive.keepaliveinterval = 5000; /*發送keepalive心跳時間間隔-單位為毫秒*/

    in_keep_alive.keepalivetime = 1000; /*多長時間沒有報文開始發送keepalive心跳包-單位為毫秒*/


    ret = WSAIoctl(socket, SIO_KEEPALIVE_VALS, (LPVOID)&in_keep_alive, ul_in_len,

        (LPVOID)&out_keep_alive, ul_out_len, &ul_bytes_return, NULL, NULL);


    if (ret == SOCKET_ERROR)

    {

        printf("WSAIoctl failed: %d \n", WSAGetLastError());

        return -1;

    }


    return 0;
}

以TCP 客戶端為例:

SHORT CClientCtrl::connetServer(LPCTSTR strAddr,USHORT portNum)
{
    AFX_MANAGE_STATE(AfxGetStaticModuleState());

    // TODO: Add your dispatch handler code here
    m_addr = strAddr;
     USES_CONVERSION;
     char *pCharAddr = T2A(m_addr);
    
    WSADATA wsd;
    WSAStartup(MAKEWORD(2, 2), &wsd);
    m_sockClient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    SOCKADDR_IN  ClientAddr;

    ClientAddr.sin_family = AF_INET;
    ClientAddr.sin_addr.S_un.S_addr = inet_addr(pCharAddr);//
    ClientAddr.sin_port = htons(portNum);//m_port
    int n = 0;
    n = connect(m_sockClient, (struct sockaddr*)&ClientAddr, sizeof(ClientAddr));
    if (n == SOCKET_ERROR) {
        
        return -1;
    }

    //set keepAlive  
    n = socket_tcp_alive(m_sockClient);//設置keepalive
    if (n <0 )
    {
        return -1;
    }
    
    CreateThread(NULL, 0, &recvFromServer, (LPVOID)this, 0, NULL);

    return 0;
}

 

DWORD WINAPI  recvFromServer(LPVOID lpParameter)
{
    char RecvBuf[MaxBufSize];
    CClientCtrl* pParent = (CClientCtrl*)lpParameter;
    SOCKET *ClientSocket = &(pParent->m_sockClient);
    int iLength;
    while (true) {
        memset(RecvBuf, '\0', sizeof(RecvBuf));
        iLength = recv(*ClientSocket, RecvBuf, MaxBufSize, 0);
        if (iLength <=0) //error   
        {

            strcpy(RecvBuf,"ERROR");
            ::SendMessage(pParent->m_hWnd,WM_RECVMSG,(WPARAM)RecvBuf,0);

            break;
        }
        else
        {
            ::SendMessage(pParent->m_hWnd,WM_RECVMSG,(WPARAM)RecvBuf,0);

        }
        


    }
    return 0;
}

  recv函數的返回值能反應網絡的狀態;

   iLength = recv(*ClientSocket, RecvBuf, MaxBufSize, 0);

如果服務器端正常關閉socket ,那么recv 的返回值iLength 為0;這種情況即使不設置keepAlive 也是可以正常返回的;

如果出現暴力關閉,比如網線被拔,服務器程序強行關閉,recv 的返回值iLength 為-1; 如果不設置keepAlive 的話,這里是不會有任何反應的。 

 

參考文獻:http://blog.csdn.net/embedded_sky/article/details/42077321


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM