STM32F407 LWIP掉线重连
STM32F407 LWIP掉线重连STM32CUBE配置(简略)网卡配置(注意网卡复位引脚)LWIP配置TCP/IP 连接自动重连的实现Lwip协议栈TCP保活(KeepAlive)设定自动重连流程简介代码实现STM32CUBE配置(简略)请根据硬件自行百度网卡、lwip、freertos配置网卡配置(注意网卡复位引脚)LWIP配置这里使用静态IP地址,注意一定要把LWIP_NETIF_LINK
STM32F407 LN8720 LWIP掉线重连
STM32CUBE配置(简略)
请根据硬件自行百度网卡、lwip
、freertos
配置
网卡配置(注意网卡复位引脚)
LWIP配置
这里使用静态IP地址,注意一定要把LWIP_NETIF_LINK_CALLBACK选上,当连接状态改变时,可以进入回调函数,里面可以做一点自己的事情。
TCP/IP 连接
自动重连的实现
本例使用单片机做
client
,电脑做server
,利用LWIP
的netconn api
进行tcp/ip
通讯,其实知道原理后改用socket
或者rawapi
也是一样的,lwip
更推荐用netconn
。
ping
通后只做Tcp/IP
连接是非常容易的。但是实际应用场合,仅在开机阶段发起连接是不够的,单片机还需要具备检测连接状态的能力,比如网线被拔或服务器端down
掉等异常。类似需求很常见,墙内外找个遍,没有一个公开的好的解决方案。
比较官方的解释是为了维持lwip
的轻量级,没做restart
功能,也就是说一旦启动了connect
操作,即使失败也不能利用原来的资源进行第二次连接,有点坑。
跟踪了一下,原因在于连接失败后会进入错误回调函数,netconn
结构体成员tcp
会被销毁掉。想重连只能是魔改协议栈或者重新new
一个netconn
,再次走tcpip
连接流程。lwip
这种复杂协议,魔改很不靠谱,那么只剩一种选择。
有一点需要注意:回调函数内部只销毁tcp资源,用户函数内的netconn
结构体实例需要手动销毁,不然连续4
-6
次connect
不成功就会内存申请失败,程序会死在一个循环里面。
从逻辑上讲,没连上就应该定时重连,连接失败后进入回调函数销毁连接,看起来总有些粗暴,但是没办法,就是这种设定。。。
Lwip协议栈TCP保活(KeepAlive)设定
万事具备了么?no!
以上只解决了异常自动重连的问题,并不等于协议栈具备检测异常的能力。即单片机必须知道网线是在什么时候被拔掉。
有很多博客都提到KeepAlive
的开启方法,但都是详细说明怎样打开,打开了该怎么用就没说。。。
由于tcp
是可靠连接,有数据往来的时候能够检测异常,需要解决的是无数据检测。这就要用到TCP
协议的KeepAlive
功能,原理就是在空闲的时候,以一定的频率发空数据包给服务器,服务器收到后答复一个数据包,说白了又把空闲段给变成有数据往复状态了。TCP
协议栈包含KeepAlive
,lwip
协议栈这部分也没少,启用几个宏即可自动进行收发。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
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)