打印
[STM32F4]

求助关于移植lwip的RAW的TCP服务端发送数据就一直重连问题

[复制链接]
5962|4
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
王大宝|  楼主 | 2024-4-10 14:48 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
第一次写以太网的控制代码,有些问题不太清楚,希望大神帮助,移植代码是根据正点原子提供的lwip例子,下边沾上代码,希望得到帮助,main函数调用 tcp_server_handle这个方法体,使用网络调试助手进行发送指令,第一次能正常连接,但是发送一个数据之后就会一直处于重连状态并且数据也没有发上去
#include "tcpserve.h"

//TCP Server接收数据缓冲区
uint8_t tcp_server_recvbuf[TCP_SERVER_RX_BUFSIZE];       
uint8_t tcp_server_sendbuf[TCP_CLIENT_TX_BUFSIZE];
struct ip4_addr rmtipaddr_serve;
uint16_t recv_len_serve;
uint16_t send_len_serve;
uint8_t SIP_ADDRESS[4];
struct tcp_pcb *tpcb;
//TCP服务器发送数据内容
//const u8 *tcp_server_sendbuf="Explorer STM32F407 TCP Server send data\r\n";

//TCP Server 测试全局状态标记变量
//bit7:0,没有数据要发送;1,有数据要发送
//bit6:0,没有收到数据;1,收到数据了.
//bit5:0,没有客户端连接上;1,有客户端连接上了.
//bit4~0:保留
uint8_t tcp_server_flag;
void tcp_server_init(void)
{
  int remote_ip[4];
   
  recv_len_serve = 0;
  send_len_serve = 0;
   
  tcp_server_flag = 0;
  

}
void tcp_server_handle(void)
{
        static uint16_t retry = 0;
        err_t err;
        struct tcp_pcb *tcppcbconn;          //定义一个TCP服务器控制块
       
        if (tpcb == NULL) {
                tpcb= tcp_new();                                                                                                                                                                                                                                                         //创建一个新的pcb

                if (tpcb) {                                                                                                                                                                                                                                                                                        //创建成功
                        //ip_get_option(tpcb, SOF_KEEPALIVE);                                                                                                                                                                                //保持激活状态
                        err = tcp_bind(tpcb,IP_ADDR_ANY,TCP_SERVER_PORT);                        //连接到目的地址的指定端口上,当连接成功后回调tcp_client_connected()函数
                        if(err==ERR_OK)        //绑定完成
                        {
                               
                                tcppcbconn=tcp_listen(tpcb);                         //设置tcppcb进入监听状态
                                tcp_accept(tpcb,tcp_server_accept);         //初始化LWIP的tcp_accept的回调函数
                               
                        }
                        //printf("tcp connect...\r\n");
                }
        }
        if (tpcb) {
                if (++retry > MAX_RETRY_NUM) {
                        if ((tcp_server_flag & 1<<5) == 0) {                                                                                                                                                                                //未连接上,则尝试重连
                                //printf("retry again\r\n");         
                                tcp_server_connection_close(tpcb, 0);                                                                                                                                                         //关闭连接
                                tpcb = tcp_new();                                                                                                                                                                                                                                         //创建一个新的pcb
                                if (tpcb) {                                                                                                                                                                                                                                                                 //创建成功
                                        //ip_get_option(tpcb, SOF_KEEPALIVE);
                                        err = tcp_bind(tpcb,IP_ADDR_ANY,TCP_SERVER_PORT);                         //连接到目的地址的指定端口上,当连接成功后回调tcp_client_connected()函数
                                        if(err==ERR_OK)        //绑定完成
                                        {
                                               
                                                tcppcbconn=tcp_listen(tpcb);                         //设置tcppcb进入监听状态
                                                tcp_accept(tpcb,tcp_server_accept);         //初始化LWIP的tcp_accept的回调函数
                                        }
                                }
                        }
                        retry = 0;      
                }

                HAL_Delay(10);
        }
}
//lwIP tcp_accept()的回调函数
err_t tcp_server_accept(void *arg,struct tcp_pcb *newpcb,err_t err)
{
        err_t ret_err;
        struct tcp_server_struct *es;
        LWIP_UNUSED_ARG(arg);
        LWIP_UNUSED_ARG(err);
        tcp_setprio(newpcb,TCP_PRIO_MIN);//设置新创建的pcb优先级
        es=(struct tcp_server_struct*)mem_malloc(sizeof(struct tcp_server_struct)); //分配内存
        if(es!=NULL) //内存分配成功
        {
                es->state=ES_TCPSERVER_ACCEPTED;          //接收连接
                es->pcb=newpcb;
                es->p=NULL;
               
                tcp_arg(newpcb,es);
                tcp_recv(newpcb,tcp_server_recv);        //初始化tcp_recv()的回调函数
                tcp_err(newpcb,tcp_server_error);         //初始化tcp_err()回调函数
                tcp_poll(newpcb,tcp_server_poll,1);        //初始化tcp_poll回调函数
                tcp_sent(newpcb,tcp_server_sent);          //初始化发送回调函数
                  
                tcp_server_flag|=1<<5;                                //标记有客户端连上了
                SIP_ADDRESS[0]=newpcb->remote_ip.addr&0xff;                 //IADDR4
                SIP_ADDRESS[1]=(newpcb->remote_ip.addr>>8)&0xff;          //IADDR3
                SIP_ADDRESS[2]=(newpcb->remote_ip.addr>>16)&0xff;         //IADDR2
                SIP_ADDRESS[3]=(newpcb->remote_ip.addr>>24)&0xff;         //IADDR1
                ret_err=ERR_OK;
        }else ret_err=ERR_MEM;
        return ret_err;
}
//lwIP tcp_recv()函数的回调函数
err_t tcp_server_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)
{
        err_t ret_err;
        uint32_t data_len = 0;
        struct pbuf *q;
          struct tcp_server_struct *es;
        LWIP_ASSERT("arg != NULL",arg != NULL);
        es=(struct tcp_server_struct *)arg;
        if(p==NULL) //从客户端接收到空数据
        {
                es->state=ES_TCPSERVER_CLOSING;//需要关闭TCP 连接了
                es->p=p;
                ret_err=ERR_OK;
        }else if(err!=ERR_OK)        //从客户端接收到一个非空数据,但是由于某种原因err!=ERR_OK
        {
                if(p)pbuf_free(p);        //释放接收pbuf
                ret_err=err;
        }else if(es->state==ES_TCPSERVER_ACCEPTED)         //处于连接状态
        {
                if(p!=NULL)  //当处于连接状态并且接收到的数据不为空时将其打印出来
                {
                        memset(tcp_server_recvbuf,0,TCP_SERVER_RX_BUFSIZE);  //数据接收缓冲区清零
                        for(q=p;q!=NULL;q=q->next)  //遍历完整个pbuf链表
                        {
                                //判断要拷贝到TCP_SERVER_RX_BUFSIZE中的数据是否大于TCP_SERVER_RX_BUFSIZE的剩余空间,如果大于
                                //的话就只拷贝TCP_SERVER_RX_BUFSIZE中剩余长度的数据,否则的话就拷贝所有的数据
                                if(q->len > (TCP_SERVER_RX_BUFSIZE-data_len)) memcpy(tcp_server_recvbuf+data_len,q->payload,(TCP_SERVER_RX_BUFSIZE-data_len));//拷贝数据
                                else memcpy(tcp_server_recvbuf+data_len,q->payload,q->len);
                                data_len += q->len;         
                                if(data_len > TCP_SERVER_RX_BUFSIZE) break; //超出TCP客户端接收数组,跳出       
                        }
                        tcp_server_flag|=1<<6;        //标记接收到数据了
                        SIP_ADDRESS[0]=tpcb->remote_ip.addr&0xff;                 //IADDR4
                        SIP_ADDRESS[1]=(tpcb->remote_ip.addr>>8)&0xff; //IADDR3
                        SIP_ADDRESS[2]=(tpcb->remote_ip.addr>>16)&0xff;//IADDR2
                        SIP_ADDRESS[3]=(tpcb->remote_ip.addr>>24)&0xff;//IADDR1
                        tcp_recved(tpcb,p->tot_len);//用于获取接收数据,通知LWIP可以获取更多数据
                        pbuf_free(p);          //释放内存
                        ret_err=ERR_OK;
                }
        }else//服务器关闭了
        {
                tcp_recved(tpcb,p->tot_len);//用于获取接收数据,通知LWIP可以获取更多数据
                es->p=NULL;
                pbuf_free(p); //释放内存
                ret_err=ERR_OK;
        }
        return ret_err;
}
//lwIP tcp_err函数的回调函数
void tcp_server_error(void *arg,err_t err)
{  
        LWIP_UNUSED_ARG(err);  
        //printf("tcp error:%x\r\n",(uint32_t)arg);
        if(arg!=NULL)mem_free(arg);//释放内存
}
//lwIP tcp_poll的回调函数
err_t tcp_server_poll(void *arg, struct tcp_pcb *tpcb)
{
        err_t ret_err;
        struct tcp_server_struct *es;
        es=(struct tcp_server_struct *)arg;
        if(es!=NULL)
        {
                if(tcp_server_flag&(1<<7))        //判断是否有数据要发送
                {
                        es->p=pbuf_alloc(PBUF_TRANSPORT,send_len_serve ,PBUF_POOL);//申请内存
                        pbuf_take(es->p,(char*)tcp_server_sendbuf,send_len_serve);
                        tcp_server_senddata(tpcb,es);                 //轮询的时候发送要发送的数据
                        tcp_server_flag&=~(1<<7);                          //清除数据发送标志位
                        if(es->p!=NULL)pbuf_free(es->p);         //释放内存       
                }else if(es->state==ES_TCPSERVER_CLOSING)//需要关闭连接?执行关闭操作
                {
                        tcp_server_connection_close(tpcb,es);//关闭连接
                }
                ret_err=ERR_OK;
        }else
        {
                tcp_abort(tpcb);//终止连接,删除pcb控制块
                ret_err=ERR_ABRT;
        }
        return ret_err;
}
//lwIP tcp_sent的回调函数(当从远端主机接收到ACK信号后发送数据)
err_t tcp_server_sent(void *arg, struct tcp_pcb *tpcb, u16_t len)
{
        struct tcp_server_struct *es;
        LWIP_UNUSED_ARG(len);
        es = (struct tcp_server_struct *) arg;
        if(es->p)tcp_server_senddata(tpcb,es);//发送数据
        return ERR_OK;
}
//此函数用来发送数据
void tcp_server_senddata(struct tcp_pcb *tpcb, struct tcp_server_struct *es)
{
        struct pbuf *ptr;
        uint16_t plen;
        err_t wr_err=ERR_OK;
         while((wr_err==ERR_OK)&&es->p&&(es->p->len<=tcp_sndbuf(tpcb)))
         {
                ptr=es->p;
                wr_err=tcp_write(tpcb,ptr->payload,ptr->len,1);
                if(wr_err==ERR_OK)
                {
                        plen=ptr->len;
                        es->p=ptr->next;                        //指向下一个pbuf
                        if(es->p)pbuf_ref(es->p);        //pbuf的ref加一
                        pbuf_free(ptr);
                        tcp_recved(tpcb,plen);                 //更新tcp窗口大小
                }else if(wr_err==ERR_MEM)es->p=ptr;
         }
}
//关闭tcp连接
void tcp_server_connection_close(struct tcp_pcb *tpcb, struct tcp_server_struct *es)
{
        tcp_close(tpcb);
        tcp_arg(tpcb,NULL);
        tcp_sent(tpcb,NULL);
        tcp_recv(tpcb,NULL);
        tcp_err(tpcb,NULL);
        tcp_poll(tpcb,NULL,0);
        if(es)mem_free(es);
        tcp_server_flag&=~(1<<5);//标记连接断开了
}
uint32_t TCPTimer=0;                        //TCP查询计时器
uint32_t ARPTimer=0;                        //ARP查询计时器
uint32_t lwip_localtime;                //lwip本地时间计数器,单位:ms
#define TCP_TMR_INTERVAL 250
#define ARP_TMR_INTERVAL 5000
//LWIP轮询任务
void lwip_periodic_handle()
{
#if LWIP_TCP
        //每250ms调用一次tcp_tmr()函数
  if (lwip_localtime - TCPTimer >= TCP_TMR_INTERVAL)
  {
    TCPTimer =  lwip_localtime;
    tcp_tmr();
  }
#endif
  //ARP每5s周期性调用一次
  if ((lwip_localtime - ARPTimer) >= ARP_TMR_INTERVAL)
  {
    ARPTimer =  lwip_localtime;
    etharp_tmr();
  }

#if LWIP_DHCP //如果使用DHCP的话
  //每500ms调用一次dhcp_fine_tmr()
  if (lwip_localtime - DHCPfineTimer >= DHCP_FINE_TIMER_MSECS)
  {
    DHCPfineTimer =  lwip_localtime;
    dhcp_fine_tmr();
    if ((lwipdev.dhcpstatus != 2)&&(lwipdev.dhcpstatus != 0XFF))
    {
      lwip_dhcp_process_handle();  //DHCP处理
    }
  }

  //每60s执行一次DHCP粗糙处理
  if (lwip_localtime - DHCPcoarseTimer >= DHCP_COARSE_TIMER_MSECS)
  {
    DHCPcoarseTimer =  lwip_localtime;
    dhcp_coarse_tmr();
  }  
#endif
}
extern void tcp_pcb_purge(struct tcp_pcb *pcb);        //在 tcp.c里面
extern struct tcp_pcb *tcp_active_pcbs;                        //在 tcp.c里面
extern struct tcp_pcb *tcp_tw_pcbs;                                //在 tcp.c里面  
//强制删除TCP Server主动断开时的time wait
void tcp_server_remove_timewait(void)
{
        struct tcp_pcb *pcb,*pcb2;
        uint8_t t=0;
        while(tcp_active_pcbs!=NULL&&t<200)
        {
                lwip_periodic_handle();        //继续轮询
               
                t++;
                                //等待tcp_active_pcbs为空  
                HAL_Delay(10);
        }
        pcb=tcp_tw_pcbs;
        while(pcb!=NULL)//如果有等待状态的pcbs
        {
                tcp_pcb_purge(pcb);
                tcp_tw_pcbs=pcb->next;
                pcb2=pcb;
                pcb=pcb->next;
                memp_free(MEMP_TCP_PCB,pcb2);       
        }
}
uint16_t tcp_server_recv_data(uint8_t* data, uint16_t size)
{
        uint16_t ret = 0;

        if (tcp_server_flag & 1<<6) {                                                                                                                                                                                                                                //收到数据
                memcpy(data, tcp_server_recvbuf, recv_len_serve);//void *memcpy(void *destin, void *source, unsigned n);destin-- 指向用于存储复制内容的目标数组,类型强制转换为 void* 指针。source-- 指向要复制的数据源,类型强制转换为 void* 指针。n-- 要被复制的字节数。
                ret = recv_len_serve;
                tcp_server_flag &= ~(1<<6);                                                                                                                                                                                                                                //标记数据已经被处理了.
        }
  
  return ret;
}

void tcp_server_send_data(const uint8_t* data, uint16_t size)
{
  if (tpcb) {
    tcp_write(tpcb, data, size, 1);//构造一个报文并放到控制块的发送缓冲队列中
    tcp_output(tpcb);//将发送缓冲队列中的数据发送出去
  }
}

使用特权

评论回复
沙发
ColeYao| | 2024-4-11 13:28 | 只看该作者
网上的很多例程中客户端模式的TCP通讯都有问题,建议改用UDP方式,或是可以用ESP8266实现TCP透传,那个通讯没问题,如果是作为Tcp服务端使用,那么有一个从网页监视温度的那个例程是OK的。

使用特权

评论回复
板凳
powerantone| | 2024-4-11 15:00 | 只看该作者
检查lwIP配置是否正确

使用特权

评论回复
地板
stormwind123| | 2024-4-11 16:00 | 只看该作者
网络不稳定或丢包可能导致TCP连接频繁重连。

使用特权

评论回复
5
probedog| | 2024-4-11 17:00 | 只看该作者
检查TCP连接超时和重传机制

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

2

主题

2

帖子

0

粉丝