Lwip做服务端发送数据会卡死,请各位大神指点迷津!

2019-12-09 20:02发布

本帖最后由 mii 于 2017-8-31 20:12 编辑

使用开发板平台是STM32F429 + LAN8720a,LWIP版本是1.4.1.
测试例程开发板自己带例程,主要移植了ST的ETH库。测试例程实现在裸跑情况下开发板建立一个服务端,使用TCP调试助手连接,发送、接收同样数据的回环测试。
下面是接收回调函数,开发板例程原程序,中文注释为返回接收到数据。
  1. static err_t tcp_echoserver_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)
  2. {
  3. #ifdef SERIAL_DEBUG
  4.         char *recdata=0;
  5. #endif
  6.        
  7.   struct tcp_echoserver_struct *es;
  8.   err_t ret_err;

  9.   LWIP_ASSERT("arg != NULL",arg != NULL);
  10.   
  11.   es = (struct tcp_echoserver_struct *)arg;
  12.   
  13.   /* if we receive an empty tcp frame from client => close connection */
  14.   if (p == NULL)
  15.   {
  16.     /* remote host closed connection */
  17.     es->state = ES_CLOSING;
  18.     if(es->p == NULL)
  19.     {
  20.        /* we're done sending, close connection */
  21.        tcp_echoserver_connection_close(tpcb, es);
  22.     }
  23.     else
  24.     {
  25.       /* we're not done yet */
  26.       /* acknowledge received packet */
  27.       tcp_sent(tpcb, tcp_echoserver_sent);
  28.       
  29.       /* send remaining data*/
  30.       tcp_echoserver_send(tpcb, es);
  31.     }
  32.     ret_err = ERR_OK;
  33.   }   
  34.   /* else : a non empty frame was received from client but for some reason err != ERR_OK */
  35.   else if(err != ERR_OK)
  36.   {
  37.     /* free received pbuf*/
  38.     es->p = NULL;
  39.     pbuf_free(p);
  40.     ret_err = err;
  41.   }
  42.   else if(es->state == ES_ACCEPTED)
  43.   {
  44.     /* first data chunk in p->payload */
  45.     es->state = ES_RECEIVED;
  46.    
  47.     /* store reference to incoming pbuf (chain) */
  48.     es->p = p;
  49.    
  50.     /* initialize LwIP tcp_sent callback function */
  51.     tcp_sent(tpcb, tcp_echoserver_sent);
  52.                
  53. #ifdef SERIAL_DEBUG
  54.                         recdata=(char *)malloc(p->len*sizeof(char));
  55.                         if(recdata!=NULL)
  56.                         {
  57.                                 memcpy(recdata,p->payload,p->len);
  58.                                 printf("upd_rec:%s",recdata);
  59.                         }
  60.                         free(recdata);
  61. #endif   
  62.                
  63.     /* send back the received data (echo) */
  64.     tcp_echoserver_send(tpcb, es);
  65.    
  66.     ret_err = ERR_OK;
  67.   }
  68.   else if (es->state == ES_RECEIVED)
  69.   {
  70.     /* more data received from client and previous data has been already sent*/
  71.     if(es->p == NULL)
  72.     {
  73.       es->p = p;
  74.                        
  75. #ifdef SERIAL_DEBUG
  76.                         recdata=(char *)malloc(p->len*sizeof(char));
  77.                         if(recdata!=NULL)
  78.                         {
  79.                                 memcpy(recdata,p->payload,p->len);
  80.                                 printf("upd_rec:%s",recdata);
  81.                         }
  82.                         free(recdata);
  83. #endif
  84.                
  85.    /* send back received data */
  86.       tcp_echoserver_send(tpcb, es);//返回接收到数据
  87.     }
  88.     else
  89.     {
  90.       struct pbuf *ptr;

  91.       /* chain pbufs to the end of what we recv'ed previously  */
  92.       ptr = es->p;
  93.       pbuf_chain(ptr,p);
  94.     }
  95.     ret_err = ERR_OK;
  96.   }
  97.   
  98.   /* data received when connection already closed */
  99.   else
  100.   {
  101.     /* Acknowledge data reception */
  102.     tcp_recved(tpcb, p->tot_len);
  103.    
  104.     /* free pbuf and do nothing */
  105.     es->p = NULL;
  106.     pbuf_free(p);
  107.     ret_err = ERR_OK;
  108.   }
  109.   return ret_err;
  110. }
复制代码
以上回环测试中,随便发送一个字符串,1ms发送接收都不会卡死。
然而修改后,注意中文注释。只想实现只要接收数据就发送一个数组数据。下面是修改后程序
  1. static err_t tcp_echoserver_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)
  2. {
  3.         char HeartDatSend[5] = {0x01,0x02,0x03,0x04,0x05};
  4. #ifdef SERIAL_DEBUG
  5.         char *recdata=0;
  6. #endif
  7.        
  8.         struct pbuf *spbuf;
  9.        
  10.   struct tcp_echoserver_struct *es;
  11.   err_t ret_err;

  12.   LWIP_ASSERT("arg != NULL",arg != NULL);
  13.   
  14.   es = (struct tcp_echoserver_struct *)arg;
  15.   
  16.   /* if we receive an empty tcp frame from client => close connection */
  17.   if (p == NULL)
  18.   {
  19.     /* remote host closed connection */
  20.     es->state = ES_CLOSING;
  21.     if(es->p == NULL)
  22.     {
  23.        /* we're done sending, close connection */
  24.        tcp_echoserver_connection_close(tpcb, es);
  25.     }
  26.     else
  27.     {
  28.       /* we're not done yet */
  29.       /* acknowledge received packet */
  30.       tcp_sent(tpcb, tcp_echoserver_sent);
  31.       
  32.       /* send remaining data*/
  33.       tcp_echoserver_send(tpcb, es);
  34.     }
  35.     ret_err = ERR_OK;
  36.   }   
  37.   /* else : a non empty frame was received from client but for some reason err != ERR_OK */
  38.   else if(err != ERR_OK)
  39.   {
  40.     /* free received pbuf*/
  41.     es->p = NULL;
  42.     pbuf_free(p);
  43.     ret_err = err;
  44.   }
  45.   else if(es->state == ES_ACCEPTED)
  46.   {
  47.     /* first data chunk in p->payload */
  48.     es->state = ES_RECEIVED;
  49.    
  50.     /* store reference to incoming pbuf (chain) */
  51.     es->p = p;
  52.    
  53.     /* initialize LwIP tcp_sent callback function */
  54.     tcp_sent(tpcb, tcp_echoserver_sent);
  55.                
  56. #ifdef SERIAL_DEBUG
  57.                         recdata=(char *)malloc(p->len*sizeof(char));
  58.                         if(recdata!=NULL)
  59.                         {
  60.                                 memcpy(recdata,p->payload,p->len);
  61.                                 printf("upd_rec:%s",recdata);
  62.                         }
  63.                         free(recdata);
  64. #endif   
  65.                
  66.     /* send back the received data (echo) */
  67.     tcp_echoserver_send(tpcb, es);
  68.    
  69.     ret_err = ERR_OK;
  70.   }
  71.   else if (es->state == ES_RECEIVED)
  72.   {
  73.     /* more data received from client and previous data has been already sent*/
  74.     if(es->p == NULL)
  75.     {
  76.       es->p = p;
  77.                        
  78. #ifdef SERIAL_DEBUG
  79.                         recdata=(char *)malloc(p->len*sizeof(char));
  80.                         if(recdata!=NULL)
  81.                         {
  82.                                 memcpy(recdata,p->payload,p->len);
  83.                                 printf("upd_rec:%s",recdata);
  84.                         }
  85.                         free(recdata);
  86. #endif
  87.                        
  88.                         spbuf = pbuf_alloc(PBUF_TRANSPORT,256,PBUF_RAM);//开一个新缓冲
  89.                         spbuf->payload = HeartDatSend;//装载数据
  90.                         spbuf->len = sizeof(HeartDatSend)/sizeof(HeartDatSend[0]);//设置发送长度
  91.                         spbuf->tot_len = sizeof(HeartDatSend)/sizeof(HeartDatSend[0]);//设备总长度
  92.                         es->p = spbuf;//将缓冲更新到要发送PCB中
  93.                
  94.       /* send back received data */
  95.       tcp_echoserver_send(tpcb, es);//发送
  96.                         // tcp_echoserver_send(es->pcb, es);
  97.                         pbuf_free(spbuf);//释放缓冲区
  98.     }
  99.     else
  100.     {
  101.       struct pbuf *ptr;

  102.       /* chain pbufs to the end of what we recv'ed previously  */
  103.       ptr = es->p;
  104.       pbuf_chain(ptr,p);
  105.     }
  106.     ret_err = ERR_OK;
  107.   }
  108.   
  109.   /* data received when connection already closed */
  110.   else
  111.   {
  112.     /* Acknowledge data reception */
  113.     tcp_recved(tpcb, p->tot_len);
  114.    
  115.     /* free pbuf and do nothing */
  116.     es->p = NULL;
  117.     pbuf_free(p);
  118.     ret_err = ERR_OK;
  119.   }
  120.   return ret_err;
  121. }
复制代码
然而上面测试中,可能有十次回环左右是成功的,然后就会卡死了协议栈。用WireSark捕捉出来的数据是发生多次重连,下面是截图
微信截图_20170831200527.png (61.32 KB, 下载次数: 0) 下载附件 2017-8-31 20:07 上传

就以上情况,摸索了好几天都没想明白,请教一下怎么解决这种情况?
友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。
该问题目前已经被作者或者管理员关闭, 无法添加新回复
10条回答
sunjun
1楼-- · 2019-12-09 20:48
 精彩回答 2  元偷偷看……
mii
2楼-- · 2019-12-09 21:03
sunjun 发表于 2017-8-31 20:39
pbuf不是像你这样玩的
用pbuf_take向pbuf拷贝数据,结构体内的参数不要随意去修改,pbuf填充和读取数据都有 ...

明白,我试试
mii
3楼-- · 2019-12-10 01:51
sunjun 发表于 2017-8-31 20:39
pbuf不是像你这样玩的
用pbuf_take向pbuf拷贝数据,结构体内的参数不要随意去修改,pbuf填充和读取数据都有 ...

感谢大神指点,确实要用PBUF的专用函数就不会出现问题。感谢,发送部分修改如下,
  1. spbuf = pbuf_alloc(PBUF_TRANSPORT,strlen((char*)HeartDatSend),PBUF_RAM);
  2.                 pbuf_take(es->p, (char*)HeartDatSend, strlen((char*)HeartDatSend));
  3.                        
  4.                
  5.       /* send back received data */
  6.       tcp_echoserver_send(tpcb, es);
  7.                 pbuf_free(spbuf);
复制代码
mii
4楼-- · 2019-12-10 04:52
sunjun 发表于 2017-8-31 20:39
pbuf不是像你这样玩的
用pbuf_take向pbuf拷贝数据,结构体内的参数不要随意去修改,pbuf填充和读取数据都有 ...

大神,上次使用PBUF_take这个函数装载是没有问题,但发送数据长度不对。就是我只复制了5个字节,但会发出11个字节数据。
单步调试发现这个函数只会简单对初始的缓冲区域进行覆盖,不会调整发送长度 。
我自己去手动调整发送长度的话,发送一会,接收窗口就变成0了。
这个应该怎么解决
sunjun
5楼-- · 2019-12-10 07:12
pbuf的数据长度是你申请的时候就确定了(pbuf_alloc),你需要多长就申请多长,与你填充多长没有关系
mii
6楼-- · 2019-12-10 09:03
 精彩回答 2  元偷偷看……

一周热门 更多>