【用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 资源
	 }
  }
}

到此问题解决。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值