STM32F407 LWIP掉线重连

STM32CUBE配置(简略)

请根据硬件自行百度网卡、lwipfreertos配置

网卡配置(注意网卡复位引脚)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

LWIP配置

在这里插入图片描述
这里使用静态IP地址,注意一定要把LWIP_NETIF_LINK_CALLBACK选上,当连接状态改变时,可以进入回调函数,里面可以做一点自己的事情。
在这里插入图片描述

TCP/IP 连接

自动重连的实现

本例使用单片机做client,电脑做server,利用LWIPnetconn api进行tcp/ip通讯,其实知道原理后改用socket或者rawapi也是一样的,lwip更推荐用netconn
ping通后只做Tcp/IP连接是非常容易的。但是实际应用场合,仅在开机阶段发起连接是不够的,单片机还需要具备检测连接状态的能力,比如网线被拔或服务器端down掉等异常。类似需求很常见,墙内外找个遍,没有一个公开的好的解决方案。
比较官方的解释是为了维持lwip的轻量级,没做restart功能,也就是说一旦启动了connect操作,即使失败也不能利用原来的资源进行第二次连接,有点坑。
跟踪了一下,原因在于连接失败后会进入错误回调函数,netconn结构体成员tcp会被销毁掉。想重连只能是魔改协议栈或者重新new一个netconn,再次走tcpip连接流程。lwip这种复杂协议,魔改很不靠谱,那么只剩一种选择。
有一点需要注意:回调函数内部只销毁tcp资源,用户函数内的netconn结构体实例需要手动销毁,不然连续4-6connect不成功就会内存申请失败,程序会死在一个循环里面。
从逻辑上讲,没连上就应该定时重连,连接失败后进入回调函数销毁连接,看起来总有些粗暴,但是没办法,就是这种设定。。。

Lwip协议栈TCP保活(KeepAlive)设定

万事具备了么?no!
以上只解决了异常自动重连的问题,并不等于协议栈具备检测异常的能力。即单片机必须知道网线是在什么时候被拔掉。
有很多博客都提到KeepAlive的开启方法,但都是详细说明怎样打开,打开了该怎么用就没说。。。
由于tcp是可靠连接,有数据往来的时候能够检测异常,需要解决的是无数据检测。这就要用到TCP协议的KeepAlive功能,原理就是在空闲的时候,以一定的频率发空数据包给服务器,服务器收到后答复一个数据包,说白了又把空闲段给变成有数据往复状态了。TCP协议栈包含KeepAlivelwip协议栈这部分也没少,启用几个宏即可自动进行收发。lwip/opt.h添加几个宏:

#define  LWIP_TCP_KEEPALIVE       1             //激活keepalive
#define  TCP_KEEPIDLE_DEFAULT     2000UL        //2秒内连接双方都无数据,则发起保活探测
#define  TCP_KEEPINTVL_DEFAULT    1000UL        //1秒发送一次保活探测
#define  TCP_KEEPCNT_DEFAULT      3UL           //3次保活探测无数据则进入错误回调函数

编码时,在完成连接后,在加入为其tcp成员加入SPF_KEEPALIVE属性。

struct netconn *xNetConn = NULL;
/*
此处省略 初始化 连接细节
*/
xNetConn->pcb.tcp->so_options |= SOF_KEEPALIVE;    //保活设定,实际上是tcp的一个选项

自动重连流程简介

自动化流程为:
拔掉网线------进入回调函数销毁tcp资源------主程序while循环连接出错------主程序销毁netconn资源------主程序实例化新的netconn资源------再次连接
如此往复…
这一部分内容就是要使tcp客户端成为一只打不死的小强

代码实现

网络连接文件

#include "main.h"
#include "lwip/opt.h"
#include "lwip/sys.h"
#include "lwip/api.h"
#include "ip_addr.h"
#include "netbuf.h"
#include "lwip.h"
#include "tcp.h"
#include "usb_device.h"
#include "usbd_cdc_if.h"

#include <string.h>

uint8_t *data;                               //TCP客户端接收数据缓冲区
void tcpecho_thread(void)
{
  struct netconn *xNetConn = NULL;
  struct netbuf *buf;
  err_t err,err_rev;
  unsigned short len;
  
  //初始化 IP结构变量
  ip_addr_t remote_ip = IPADDR4_INIT(IPADDR_ANY);

  //绑定 IP地址  
  IP4_ADDR( &remote_ip, 192, 168, 31, 30);
  xNetConn = netconn_new(NETCONN_TCP);  //创建套接字
  while(1)
  {
    err = netconn_connect(xNetConn, &remote_ip, 8881); //正常连接这里只会执行一次,阻塞在下面的netconn_recv
    /*
      保活设定:保活的含义是让单片机能够知道网络断了,进入错误处理程序。

      1.在tcpecho.h中进行如下设定
      #define  LWIP_TCP_KEEPALIVE       1             //激活keepalive
      #define  TCP_KEEPIDLE_DEFAULT     2000UL        //2秒内连接双方都无数据
      #define  TCP_KEEPINTVL_DEFAULT    1000UL        //1秒发送一次保活探测
      #define  TCP_KEEPCNT_DEFAULT      3UL           //3次保活探测无数据则进入错误回调函数
      2.为tcp的so_options加上SOF_KEEPALIVE选项
    */
    xNetConn->pcb.tcp->so_options |= SOF_KEEPALIVE;    //保活设定,实际上是tcp的一个选项
    if (err == ERR_OK)
    {
      while ((err_rev = netconn_recv(xNetConn, &buf)) == ERR_OK) //阻塞在这里
      {
        do
        {
          netbuf_data(buf, &data, &len);   //获取消息内容 消息长度
          usb_printf("长度:%d 报文:%s",len,data);
          netconn_write(xNetConn, data, len,  //将消息 原样回复服务器
                    NETCONN_NOCOPY);
        } while (netbuf_next(buf) >= 0);
        netbuf_delete(buf);    //清空消息结构
      }
    }
    else
    {
      netconn_close(xNetConn);                 //一定要关闭
      netconn_delete(xNetConn);                //一定要删除,否则在malloc xNetConn时候,4~6次就申请不到导致直接error退出
      xNetConn = netconn_new(NETCONN_TCP);     //重新建立新连接
      vTaskDelay(2000);
    }
  }
}

任务文件

void TestTask(void const * argument)
{
  tcpecho_thread();
  for(;;)
  {
    osDelay(2000);		
  }
}

不插网线启动板子,再次插入网线不能连接网络,解决这个问题只需实现回调函数即可,这个函数在源码中是弱声明,需要自己填充实现。这里只实现网线插拔功能的处理

void ethernetif_notify_conn_changed(struct netif *netif)
{
  if(netif_is_link_up(netif) && !netif_is_up(netif))
  {
    netif_set_up(netif);
  }
  else
  {
    
  }
}

参开链接:https://blog.csdn.net/monei3525/article/details/108941383

### STM32H7 LwIP 实现自动连的方法和配置 为了确保STM32H7在使用LwIP时能够稳定地处理连接中断并实现自动连,可以采取以下措施: #### 配置TCP KeepAlive机制 启用TCP KeepAlive功能有助于检测远端主机是否仍然在线。当KeepAlive探测失败时,应用程序可以触发新建立连接的操作。 ```c // 设置TCP选项以开启keepalive特性 tcp_set_keepalive(newpcb, KEEPALIVE_IDLE_TIME, KEEPALIVE_INTERVAL, KEEPALIVE_COUNT); ``` 上述代码片段展示了如何设置TCP keepalive参数[^1]。 #### 设计断线回调函数 定义一个专门用于监听连接状态变化的回调函数,在此函数内部判断如果发生异常关闭,则启动一个新的连接尝试逻辑。 ```c err_t tcp_connection_err(void *arg, struct tcp_pcb *tpcb, err_t err) { if (ERR_ABRT == err || ERR_RST == err) { // 当收到RST包或被主动终止时 printf("Connection was reset or aborted\n"); // 尝试启连接... start_tcp_client(); } return ERR_OK; } ``` 这段C语言程序实现了针对特定错误类型的响应行为,并调用了`start_tcp_client()`来发起新的连接请求[^2]。 #### 定期轮询连接状态 除了依赖于被动接收事件通知外,还可以周期性地查询当前会话的状态,一旦发现处于非活动状态下就立即着手恢复链路正常工作。 ```c void check_and_reconnect() { static uint32_t last_check_time = 0; if ((HAL_GetTick() - last_check_time) >= CONNECTION_CHECK_PERIOD_MS) { last_check_time = HAL_GetTick(); if (!is_connected()) { stop_current_session(); /* 清理旧资源 */ start_new_session_attempt();// 发起新一次握手过程 } } } /* 调度器任务中定期调用该函数 */ sys_timeout(CONNECTION_CHECK_PERIOD_MS, (sys_timeout_handler_t)check_and_reconnect, NULL); ``` 这里给出了一种基于定时器驱动的方式来进行健康检查以及必要时候实施再连接操作的例子。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值