首页 > 其他分享 >STM32被拔网线 LWIP的TCP无法重连解决方案

STM32被拔网线 LWIP的TCP无法重连解决方案

时间:2024-07-20 14:27:52浏览次数:11  
标签:LWIP recv 网线 TCP 任务 tcp 被拔 OS

目录

一、问题描述

二、项目构成

三、问题解决

1.问题代码

2.解决思路

3.核心代码: 

四、完整代码

1.监测网口插入拔出任务

2.TCP任务

3.创建tcp任务

4.删除tcp任务

五、总结


一、问题描述

最近遇到一个问题,就是我的stm32设备作为tcp客户端和上位机交互,如果在连接过程中网线被拔断,等待时间稍微长一点再插上的话,tcp将不能再连接到服务器端,除非重启设备,所以我开始研究怎么解决这个lwip的小问题。

二、项目构成

MCU : STM32F429IGTx

网口芯片 :LAN8720

操作系统 :UCOSIII

协议栈:LWIP

调试工具:sscom5.13.1(可开启TCP服务端)

三、问题解决

1.问题代码

我们写了一个socket的tcp客户端作为一个单独的任务执行,recv这个函数阻塞,没数据的时候一直被阻塞,但是不影响其它任务,有了数据发过来,或者正常的tcp断开,recv函数就会收到数据往下执行,但是这时候我们遇到了一个问题,那就是TCP连接状态下,网线被拔出,recv这个函数没有做任何的反应,所以这便导致了recv这个函数一直被阻塞,插上网线以后不能重新像服务器进行tcp连接,理想状态下是recv函数应该也像正常tcp断开那样给我返回一个信号,那样我就知道tcp中断了,就去循环重新获取TCP连接,可是并没有,我们设备安装在现场难免会有网线被拔出的情况,一拔出再插入tcp就连接不上了说不过去,所以只能自己想办法解决这个问题。

2.解决思路

一开始的解决思路就是在tcp的recv下面加一个检测网线是否被插入的判断,如果网线被拔出的话,也break,跳出当前while去上一级while里面进行tcp连接,可是忽略了recv函数阻塞的问题,网线被拔出recv没有数据根本不往下执行,如果是netconn不阻塞的那种倒是可以,所以这个方案否了。

后来琢磨recv不是阻塞么,不如重新创建一个任务检测网口的网线插入状态,把这个tcp任务重新启动呢,最开始想到了挂起再恢复,后来发现恢复以后任务还会继续在阻塞里面,解决不了问题。想了想只能是拔出网线后删除tcp任务再重新创建了,为避免资源浪费,检测到网线拔出就删除tcp任务,检测到网线插入就创建tcp任务

3.核心代码: 

HAL库  LAN8720_ReadPHY(PHY_BSR) & PHY_LINKED_STATUS

标准库   ETH_ReadPHYRegister(LAN8720_PHY_ADDRESS, PHY_BSR) & PHY_Linked_Status)

如果网线是插入状态 代码的结果就是1

如果网线是拔出状态 代码的结果就是0

当tcp建立连接以后,就一直去判断网线有没有被拔出,如果被拔出了,就删除tcp任务。当tcp没有建立连接的时候,就一直去判断网线有没有被插入,插入的话就创建tcp任务,注意代码逻辑不要多次删除或者创建同一任务导致系统崩溃

四、完整代码

1.监测网口插入拔出任务

u8 TCP_CONNECT_FLAG=0;//TCP连接状态 0是未连接 1是已连接 2是重新创建了任务待连接

//1.监测网口插入拔出任务
void key_task(void *pdata)
{
	u8 res;
	OS_ERR err;	
	while(1)
	{
		/**key = KEY_Scan(0);
		if(key==KEY0_PRES) //发送数据
		{
			LED0 = !LED0;
		}
		**/
		if(TCP_CONNECT_FLAG==1){
			if(!(LAN8720_ReadPHY(PHY_BSR) & PHY_LINKED_STATUS)){
				//删除tcp任务
				TCP_CONNECT_FLAG=0;
				tcp_deletetask();
				
			}
		}else if(TCP_CONNECT_FLAG==0){
			if((LAN8720_ReadPHY(PHY_BSR) & PHY_LINKED_STATUS)){
				//打开tcp任务
				TCP_CONNECT_FLAG=2;
				tcp_starttask();
				
			}
		}
		OSTimeDlyHMSM(0,0,0,100,OS_OPT_TIME_HMSM_STRICT,&err); //延时2s
	}
}

2.TCP任务

#define PORT 5001
#define RECV_DATA (1024)
#define SERV_IP_ADDR "192.168.0.222"
#define SERV_PORT 8088
unsigned char rec_buffers[1024]={"0X66,0x14,0x97,0x0F,0x1D,0xEA\n"}; 
unsigned char rec_buffers2[1024]={"\n"}; 
extern u8 TCP_CONNECT_FLAG;
int sock=-1;
	

void tcp_thread(void *arg)
{
 OS_ERR err;
 
	int block = 1;
  struct sockaddr_in Serv_addr;
  //char*recv_data;
  int recv_data_len;
  /*为recv_data申请内存空间 申请成功返回内存空间首地址 失败返回NULL*/
  //recv_data=(char*)malloc(RECV_DATA);
  //if(recv_data==NULL){
      //printf("Mallo memory failed\r\n");
 // }
  while(1){
		if(sock!=-1){
			closesocket(sock);
			sock=-1;
		}
    /* 为sockaddr_in结构体成员赋值,用于以下的connect绑定  参数protocol在TCP/TCP两种协议下均为0  */
    /*套接字申请成功返回Socket描述符(int类型) 失败返回-1*/
    sock=socket(AF_INET,SOCK_STREAM,0);
    if(sock<0)
    {
     // printf("Socket error\n");
        OSTimeDlyHMSM(0,0,0,10,OS_OPT_TIME_HMSM_STRICT,&err);
      continue;
    }
		
		//ioctlsocket(sock,FIONBIO,&block);  
    /*TCP/IP – IPv4*/
    Serv_addr.sin_family=AF_INET;
    /*绑定远端服务器的端口*/
    Serv_addr.sin_port=htons(SERV_PORT);
    /*绑定远端服务器的ip*/
    Serv_addr.sin_addr.s_addr=inet_addr(SERV_IP_ADDR);
	  /* 清空sockaddr_in结构体内存空间   sin_zero是为了让sockaddr与sockaddr_in两个数据结构保持大小相同而保留的空字节 */
    memset(&(Serv_addr.sin_zero), 0, sizeof(Serv_addr.sin_zero)); 
     /* 连接远端服务器 */
    if (connect(sock, (struct sockaddr *)&Serv_addr, sizeof(struct sockaddr)) == -1) 
    {
			//printf("Connect failed!\n");
			closesocket(sock);
			OSTimeDlyHMSM(0,0,0,10,OS_OPT_TIME_HMSM_STRICT,&err);
			continue;
    }
		TCP_CONNECT_FLAG=1;
    //printf("Connect to tcp server successful!\n");   
     while(1)
    {	
			/* 成功接收到数据,返回接收的数据长度 */
      recv_data_len = recv(sock, rec_buffers2, RECV_DATA, 0);
			if (recv_data_len <= 0){ 
        break; 
			}else{
				write(sock,rec_buffers,1024);
			}
			/* 串口打印接收的数据内容 */
			//printf("recv:%s\n",recv_data);
			/* 发送数据内容 */
			
			OSTimeDlyHMSM(0,0,0,5,OS_OPT_TIME_HMSM_STRICT,&err);			
    }
		OSTimeDlyHMSM(0,0,0,5,OS_OPT_TIME_HMSM_STRICT,&err);
		  
  }
 }

3.创建tcp任务

void tcp_starttask(){
	OS_ERR err;
	
	CPU_SR_ALLOC();
	
	OS_CRITICAL_ENTER();//进入临界区
	
	OSTaskCreate((OS_TCB 	* )&TcpTaskTCB,		
				 (CPU_CHAR	* )"tcp task", 		
                 (OS_TASK_PTR )tcp_thread, 			
                 (void		* )0,					
                 (OS_PRIO	  )TCP_PRIO,     
                 (CPU_STK   * )&TCP_TASK_STK[0],	
                 (CPU_STK_SIZE)TCP_STK_SIZE/10,	
                 (CPU_STK_SIZE)TCP_STK_SIZE,		
                 (OS_MSG_QTY  )0,					
                 (OS_TICK	  )0,					
                 (void   	* )0,					
                 (OS_OPT      )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR,
                 (OS_ERR 	* )&err);
	
	OS_CRITICAL_EXIT();	//退出临界区
}

4.删除tcp任务

void tcp_deletetask(){
	OS_ERR err;
	CPU_SR_ALLOC();
	
	OS_CRITICAL_ENTER();//进入临界区
	OSTaskDel((OS_TCB 	* )&TcpTaskTCB,&err);
	OS_CRITICAL_EXIT();	//退出临界区
	
}

五、总结

算是解决了网线拔出再插入以后tcp不能重新建立连接的问题,可能方法过于简单粗暴,如果大佬有更好的方法解决这个问题欢迎交流指导。

标签:LWIP,recv,网线,TCP,任务,tcp,被拔,OS
From: https://blog.csdn.net/qq_38072731/article/details/140551662

相关文章

  • keepalived绑定单播地址、非抢占模式及LVS的TCP模式的高可用【转】
    背景:keepalived默认是组播地址进行播放,且默认地址是224.0.0.18,如果配置多个keepalived主机,会导致虚拟IP地址存在冲突问题,这种问题怎么解决呢?解决办法:就是将keepalived主机的多播地址修改为单播地址,绑定固定IP地址,避免在多播模式下,通过VRRP进行广播地址,造成IP地址地址冲突。vrrp_......
  • socket 收发TCP/UDP
    一、c++个人测试记录,有问题还请指出,谢谢参考:C++开发基础之网络编程WinSock库使用详解TCP/UDPSocket开发_c++udp使用什么库-CSDN博客代码中Logger测试见文章: c++中spdlog的使用/python中logger的使用-CSDN博客1、main.cpp收发TCP信号:#include<iostream>#include<thr......
  • TCP/IP协议,以及对等网络通信原理!
    TCP/IP模型协议分层应用层:HTTP:超文本传输协议(网站访问WEB)(Apache、nginx)(IIS)FTP:文件传输协议(网络文件传输)TFTP:简单文件传输协议(交换机和路由器重装)SMTP:简单邮件传输协议(发信)POP3:邮局协议3代(收信)SNMP:简单网络管理协议(服务器监控)DNS:域名系统(域名与IP解析)传输层:TCP:传......
  • TCP协议详解
    传输控制协议(TCP,TransmissionControlProtocol)是一种面向连接的、可靠的、基于字节流的传输层通信协议。1.TCP头部格式源/目的端口:表示数据从哪个进程发送,然后发送到哪个进程去32位序列号:发送的数据按照一个字节一个编号存放进去32位确认号:用于给与对方响应,值为收到的TC......
  • 在Linux中,tcp三次握⼿的过程及原理?
    在Linux中,TCP(传输控制协议)的三次握手是建立可靠连接的重要过程。这一机制确保了客户端和服务器之间能够安全、有序地交换数据。下面将详细阐述TCP三次握手的过程及原理:一、TCP三次握手的过程TCP三次握手过程涉及客户端(通常称为“主动打开方”)和服务器(通常称为“被动打开方”)之间......
  • python的tkinter、socket库开发tcp的客户端和服务端
    一、tcp通讯流程和开发步骤1、tcp客户端和服务端通讯流程图套接字是通讯的利器,连接时要经过三次握手建立连接,断开连接要经过四次挥手断开连接。2、客户端开发流程1)创建客户端套接字2)和服务端器端套接字建立连接3)发送数据4)接收数据5)关闭客户端套接字3、服务端开发......
  • 网络编程-TCP/IP
    网络概述网络采用分而治之的方法设计,将网络的功能划分为不同的模块,以分层的形式有机组合在一起。每层实现不同的功能,其内部实现方法对外部其他层次来说是透明的。每层向上层提供服务,同时使用下层提供的服务网络体系结构即指网络的层次结构和每层所使用协议的集合两类非......
  • 【计算机网络中的TCP/IP】TCP/IP协议中的tcp与udp
    目录简单介绍一些TCP/IP协议TCP/IP协议的组成TCP/IP协议中tcp与udp的区别1.简单介绍一下TCP/IP协议         TCP/IP(TransmissionControlProtocol/InternetProtocol,传输控制协议/网际协议)是指能够在多个不同网络间实现信息传输的协议簇。TCP/IP协议不仅仅指......
  • TCP/IP网络模型详解
    在计算机网络领域,网络模型通常指的是OSI(OpenSystemsInterconnection)参考模型或TCP/IP(TransmissionControlProtocol/InternetProtocol)模型。这些模型描述了网络中数据传输的层次结构,便于理解和设计网络系统。1.OSI七层网络模型OSI(OpenSystemsInterconnection)七层网络......
  • Java中的网络编程与TCP/IP协议详解
    Java中的网络编程与TCP/IP协议详解大家好,我是微赚淘客系统3.0的小编,是个冬天不穿秋裤,天冷也要风度的程序猿!在现代软件开发中,理解和掌握网络编程以及TCP/IP协议是构建高性能网络应用的关键。本文将深入讨论Java中的网络编程技术和TCP/IP协议的工作原理与应用。一、网络编程基础......