用lwip的netconn接口的搭载TCP服务端
1、硬件基础及功能实现
- 硬件:STM32F407+LAN8720
- 系统:FreeRTOS V10.2.1+lwip
- 功能:实现TCP服务端5001端口,该端口只能运行一个客户端连接,多余连接请求都禁止连接,且支持热拔插。
- 重点问题: LWIP异常拔掉网线无法释放资源端口问题解决
因为我做超声波电源箱项目有专门上位机软件,只要支持连接一个上位机软件就可以了,如果客户不小心打开两个上位机,其中后打开上位机无法连接即可,但一定要支持热拔插。
2、lwip的存在问题(netconn_delete异常断连无法释放conn问题)
故障原代码:发现如果因为设置newconn.recv_timeout=5000; 如果拔掉网线因5s内未发送数据给到服务端,会因为超时跳出:err=ERR_TIMEOUT报错,后正常netconn_close();netconn_delete()显示正常但其实是无法释放资源,重新netconn_bind()就会报错ERR_USE:显示端口一直被占用。
//故障原代码
void TCP_Server(void *pdata)
{
err_t err;
struct pbuf *q;
struct netconn *tcp_serverconn, *newconn;
struct netbuf *recvbuf;
while (1)
{
tcp_serverconn = netconn_new(NETCONN_TCP); //新建一个netconn连接
netconn_bind(tcp_serverconn, IP_ADDR_ANY, 12345); //绑定ip和端口号
netconn_listen(tcp_serverconn); //开始监听
//阻塞等待client连接,连接成功的话会建立了一个新的netconn连接: newconn
netconn_accept(tcp_serverconn, &newconn);
newconn.recv_timeout=5000;
//程序到这里说明已经有一个client连接成功
//加入下面这2句话的原因:已经有一个client连接到server后防止其他client再连接到server上。
//不关闭这个连接的话,其他client可以成功连接到tcp_serverconn上,但是无法收发数据
//在netconn_listen()设置TCP_DEFAULT_LISTEN_BACKLOG 0x01 也不能阻止其他端口过来连接,还是下面两句代码后实在,直接关闭端口
netconn_close(tcp_serverconn); //关闭tcp_serverconn连接
netconn_delete(tcp_serverconn); //删除tcp_serverconn连接
while (1)
{
if ((err = netconn_recv(newconn, &recvbuf)) == ERR_OK)
{
//·······
netbuf_delete(recvbuf);
}else
{
//发现如果因为设置newconn.recv_timeout=5000; 如果5s内未发送数据给到服务端,会因为超时跳出:err=ERR_TIMEOUT报错,后正常netconn_close();netconn_delete()都无法释放资源
netconn_close(newconn); //关闭 newconn 连接
netconn_delete(newconn);//释放 newconn 资源
break; //退出内while1,去外层while1,
}
}
}
}
后面这篇ESP32中直接调用lwip_close无法正确释放资源,导致后续文件描述符无法正确创建
发现lwip的soket使用也会有释放资源未彻底现象。
后面疯狂找链接解决问题:
STM32 LWIP Server、Client如何判断网络异常
想不设置newconn.recv_timeout=5000;物理层或其他方式找到网口断开方式进行判断,再去人为释放端口资源。发现我中间接了交换机,用LWIP的netif接口netif_is_link_up(&gnetif)来判断是否断链接方式行不通,也不能用HAL_ETH_ReadPHYRegister()来判断网口状态。而且最后尝试了断开网线去是否释放端口资源,lwip也是因为异常断线无法释放资源,所以放弃了
其他连接及资源:
用netconn_accept建立的server被close,重建后client重连失败
AT32F437 UCOSIII系统Lwip下NETCONN编程接口TCP多客户端连接
LwIP如何对LWIP的tcp客户端连接数做限制呢
LWIP应用开发实战指南—基于STM32
3、lwip的keep_alive功能解决热拔插问题
最后找到这个lwip的keep_alive功能:LWIP使用TCP自带的KEEPALIVE功能
原来LWIP本身就有设置保活机制,利用保护机制去释放异常网线断开资源,解决:netconn_delete异常断连无法释放conn问题
4、最终TCP服务端代码
最总代码附上:
LWIP目前的版本也支持TCP_KEEPALIVE功能,但默认是关闭状态。
要使用LWIP的TCP_KEEPALIVE功能,需要如下配置:
1.修改lwipopts.h添加以下内容:
//KEEPALIVE
#define LWIP_TCP_KEEPALIVE 1
#define TCP_KEEPIDLE_DEFAULT 3000 //3秒内双方无数据则发起保活探测
#define TCP_KEEPINTVL_DEFAULT 1000 //1秒发送一次保活探测
#define TCP_KEEPCNT_DEFAULT 3 //3次探测无响应则断开
2.创建TCP连接时,需要添加以下代码
void TCP_Server(void *pdata)
{
err_t err;
struct pbuf *q;
struct netconn *tcp_serverconn, *newconn;
struct netbuf *recvbuf;
while (1)
{
tcp_serverconn = netconn_new(NETCONN_TCP); //新建一个netconn连接
tcp_serverconn ->pcb.tcp->so_options |= SOF_KEEPALIVE; //开启keep_live功能
netconn_bind(tcp_serverconn, IP_ADDR_ANY, 12345); //绑定ip和端口号
err=netconn_listen(tcp_serverconn); //开始监听
//阻塞等待client连接,连接成功的话会建立了一个新的netconn连接: newconn
netconn_accept(tcp_serverconn, &newconn);
//newconn.recv_timeout=5000; //不可取直接去掉,改用keep_alive功能
if(err== ERR_OK)
{
//程序到这里说明已经有一个client连接成功
//加入下面这2句话的原因:已经有一个client连接到server后防止其他client再连接到server上。
//不关闭这个连接的话,其他client可以成功连接到tcp_serverconn上,但是无法收发数据
//在netconn_listen()设置TCP_DEFAULT_LISTEN_BACKLOG 0x01 也不能阻止其他端口过来连接,还是下面两句代码后实在,直接关闭端口
netconn_close(tcp_serverconn); //关闭tcp_serverconn连接
netconn_delete(tcp_serverconn); //删除tcp_serverconn连接
while (1)
{
if ((err = netconn_recv(newconn, &recvbuf)) == ERR_OK)
{
//·······
netbuf_delete(recvbuf);
}else
{
//发现如果因为设置newconn.recv_timeout=5000; 如果5s内未发送数据给到服务端,会因为超时跳出:err=ERR_TIMEOUT报错,后正常netconn_close();netconn_delete()都无法释放资源
netconn_close(newconn); //关闭 newconn 连接
netconn_delete(newconn);//释放 newconn 资源
break; //退出内while1,去外层while1,
}
}
}else
{
netconn_close(newconn); //关闭 newconn 连接
netconn_delete(newconn);//释放 newconn 资源
}
}
}
到此问题解决。