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

posted on 2021-10-19 16:03  lazybee  阅读(3710)  评论(0编辑  收藏  举报