lwip_tcp client保活机制的应用

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zhaozhiyuan111/article/details/83088995

lwip_tcp Unbeatable client

项目测试记录:2018/10/16
保活机制的设置参考:
https://blog.csdn.net/allan0508/article/details/524686
(先自行了解保活机制)
LWIP中包括两个定时器函数:一个函数每 250 ms调用一次(快速定时器);另一个函数每500ms调用一次(慢速定时器)。

tcp_slowtmr函数的部分代码:

    static unsigned char Close_Wait_keep_cnt_sent = 0;
    /* Check if KEEPALIVE should be sent */
    if(ip_get_option(pcb, SOF_KEEPALIVE) &&      //如果使用了保活机制
       ((pcb->state == ESTABLISHED) ||
        (pcb->state == CLOSE_WAIT))) {
      if((u32_t)(tcp_ticks - pcb->tmr) >
         (pcb->keep_idle + TCP_KEEP_DUR(pcb)) / TCP_SLOW_INTERVAL)      //2小时+9*75秒后断开连接
      {
        LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: KEEPALIVE timeout. Aborting connection to %"U16_F".%"U16_F".%"U16_F".%"U16_F".\n",
                                ip4_addr1_16(&pcb->remote_ip), ip4_addr2_16(&pcb->remote_ip),
                                ip4_addr3_16(&pcb->remote_ip), ip4_addr4_16(&pcb->remote_ip)));

        ++pcb_remove;          //设置两个局部变量,后需删除控制块
        ++pcb_reset;           //复位连接
      }
      else if((u32_t)(tcp_ticks - pcb->tmr) >
              (pcb->keep_idle + pcb->keep_cnt_sent * TCP_KEEP_INTVL(pcb))
              / TCP_SLOW_INTERVAL)
      {
        tcp_keepalive(pcb);    //发送保活探查报文
        pcb->keep_cnt_sent++;  //保活报文数加1
        if(pcb->state == CLOSE_WAIT)   Close_Wait_keep_cnt_sent++;
        else  Close_Wait_keep_cnt_sent = 0;
      }
      if(pcb->state == CLOSE_WAIT){
          if(Close_Wait_keep_cnt_sent > pcb->keep_cnt) //close wait timeout for (pcb->keep_idle + TCP_KEEP_DUR(pcb))
          {
              ++pcb_remove;          //设置两个局部变量,后需删除控制块
              ++pcb_reset;           //复位连接
          }
      }
    }

保活机制(ip_get_option(pcb, SOF_KEEPALIVE))打开后,保证在ESTABLISHED和CLOSE_WAIT两状态下发生异常(ESTABLISHED状态的异常如网线断开,CLOSE_WAIT状态的异常如sever主动断开。),并发送完保活探查报文后(发送次数由pcb->keep_cnt决定),程序能够执行pcb_remove和pcb_reset的置位。

    /* If the PCB should be removed, do it. */
    if (pcb_remove) {
      struct tcp_pcb *pcb2;
      tcp_err_fn err_fn;
      void *err_arg;
      tcp_pcb_purge(pcb);
      /* Remove PCB from tcp_active_pcbs list. */
      if (prev != NULL) {
        LWIP_ASSERT("tcp_slowtmr: middle tcp != tcp_active_pcbs", pcb != tcp_active_pcbs);
        prev->next = pcb->next;
      } else {
        /* This PCB was the first. */
        LWIP_ASSERT("tcp_slowtmr: first pcb == tcp_active_pcbs", tcp_active_pcbs == pcb);
        tcp_active_pcbs = pcb->next;
      }

      if (pcb_reset) {
        tcp_rst(pcb->snd_nxt, pcb->rcv_nxt, &pcb->local_ip, &pcb->remote_ip,
          pcb->local_port, pcb->remote_port);
      }

      err_fn = pcb->errf;    //错误回调函数
      err_arg = pcb->callback_arg;  //错误回调函数参数
      pcb2 = pcb;
      pcb = pcb->next;
      memp_free(MEMP_TCP_PCB, pcb2);

      tcp_active_pcbs_changed = 0;
      TCP_EVENT_ERR(err_fn, err_arg, ERR_ABRT);   
      if (tcp_active_pcbs_changed) {
        goto tcp_slowtmr_start;
      }
    }

pcb_remove置位后,上面的代码会生效,重点是在这里可以加载一个错误回调函数,我们可以在错误回调函数中处理异常。然而这个错误回调函数我们需要在客户端初始化的时候给它注册。

           u_sTcp_pcb[i] = tcp_new();
           tcp_bind(u_sTcp_pcb[i], &IPAddr, pConnectMesg->wSrcPort);
           if(byCliServ == LWIP_CLIENT)
            {
                if(u_sTcp_pcb[i] != NULL)
                    ip_set_option(u_sTcp_pcb[i], SOF_KEEPALIVE);

                tcp_connect(u_sTcp_pcb[i], &RemoteIP, pConnectMesg->wDstPort, pConnectMesg->pTcpConnected); 
                tcp_arg(u_sTcp_pcb[i], (void *)pConnectMesg);   //指定网络错误回调函数的参数
                tcp_err(u_sTcp_pcb[i], echo_client_conn_err);   //注册网络错误回调函数
                tcp_recv(u_sTcp_pcb[i], pConnectMesg->pTcpRecv);  
                return u_sTcp_pcb[i];
            }

下面就该是错误回调函数的函数体了:

static void echo_client_conn_err(void *arg, err_t err)
{
    int i = 0;
    err_t ERR;
    struct ip_addr  SeverIP;
    NETCONNECTMESG * pReconnectMesg = (NETCONNECTMESG *)arg;

    if(err != ERR_ABRT)  return;
    IP4_ADDR(&SeverIP, pReconnectMesg->aDstIP[0], pReconnectMesg->aDstIP[1],
            pReconnectMesg->aDstIP[2], pReconnectMesg->aDstIP[3]);


    for(i = 0; i < LWIP_MAX_CONNECT; i++)
    {
        if(u_sTcp_pcb[i] != NULL)
        {
            ERR = tcp_close(u_sTcp_pcb[i]);
            if(ERR == ERR_OK)
                u_sTcp_pcb[i] = NULL;
        }
        if(u_sTcp_pcb[i] == NULL)
        {
            u_sTcp_pcb[i] = tcp_new();
            if(u_sTcp_pcb[i] != NULL)
                ip_set_option(u_sTcp_pcb[i], SOF_KEEPALIVE);
            tcp_bind(u_sTcp_pcb[i], IP_ADDR_ANY, pReconnectMesg->wSrcPort);

            tcp_connect(u_sTcp_pcb[i], &SeverIP, pReconnectMesg->wDstPort, pReconnectMesg->pTcpConnected);
            tcp_arg(u_sTcp_pcb[i], pReconnectMesg);
            tcp_err(u_sTcp_pcb[i], echo_client_conn_err);
            tcp_recv(u_sTcp_pcb[i], pReconnectMesg->pTcpRecv);
            return;
        }
    }
}

目前测试,client是打不死的小强。

猜你喜欢

转载自blog.csdn.net/zhaozhiyuan111/article/details/83088995
今日推荐