搜索
bottom↓
回复: 14

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

[复制链接]

出0入0汤圆

发表于 2017-8-31 20:08:49 | 显示全部楼层 |阅读模式
本帖最后由 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捕捉出来的数据是发生多次重连,下面是截图


就以上情况,摸索了好几天都没想明白,请教一下怎么解决这种情况?

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

阿莫论坛20周年了!感谢大家的支持与爱护!!

一只鸟敢站在脆弱的枝条上歇脚,它依仗的不是枝条不会断,而是自己有翅膀,会飞。

出50入8汤圆

发表于 2017-8-31 20:39:43 | 显示全部楼层
pbuf不是像你这样玩的
用pbuf_take向pbuf拷贝数据,结构体内的参数不要随意去修改,pbuf填充和读取数据都有相应的函数

出0入0汤圆

 楼主| 发表于 2017-8-31 21:01:03 | 显示全部楼层
airbox 发表于 2017-8-31 20:39
pbuf不是像你这样玩的
用pbuf_take向pbuf拷贝数据,结构体内的参数不要随意去修改,pbuf填充和读取数据都有 ...

明白,我试试

出0入0汤圆

 楼主| 发表于 2017-8-31 21:09:56 | 显示全部楼层
airbox 发表于 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);
复制代码

出0入0汤圆

 楼主| 发表于 2017-9-2 18:25:58 | 显示全部楼层
airbox 发表于 2017-8-31 20:39
pbuf不是像你这样玩的
用pbuf_take向pbuf拷贝数据,结构体内的参数不要随意去修改,pbuf填充和读取数据都有 ...

大神,上次使用PBUF_take这个函数装载是没有问题,但发送数据长度不对。就是我只复制了5个字节,但会发出11个字节数据。
单步调试发现这个函数只会简单对初始的缓冲区域进行覆盖,不会调整发送长度 。
我自己去手动调整发送长度的话,发送一会,接收窗口就变成0了。
这个应该怎么解决

出50入8汤圆

发表于 2017-9-4 11:15:48 | 显示全部楼层
pbuf的数据长度是你申请的时候就确定了(pbuf_alloc),你需要多长就申请多长,与你填充多长没有关系

出0入0汤圆

 楼主| 发表于 2017-9-5 09:34:19 | 显示全部楼层
airbox 发表于 2017-9-4 11:15
pbuf的数据长度是你申请的时候就确定了(pbuf_alloc),你需要多长就申请多长,与你填充多长没有关系 ...

我也你的同样方式测试程序,但出现刚开始的现象,发送一定数量 后就卡死,再也进不了接收回调函数里面

出0入0汤圆

发表于 2017-9-5 09:47:33 | 显示全部楼层
你那一定数量是4096吗? tcp_recved是不是这个问题  窗口值的问题 去搜索老衲五木的博客吧。

出0入0汤圆

 楼主| 发表于 2017-9-5 10:42:15 | 显示全部楼层
maoxue121225 发表于 2017-9-5 09:47
你那一定数量是4096吗? tcp_recved是不是这个问题  窗口值的问题 去搜索老衲五木的博客吧。 ...

我用WireShark捕捉开发板上的端口(服务器),发现是客户端发送多次重连。
我一开始也曾怀疑是窗口没更新,但才之客户端发了15次左右,就发生重连了,协议栈也没卡死,只是再客户端再发数据再也进不到接收函数了。
然而我做回环测试没有这个现象。回环,可以测试发送一天一夜都不会掉。

出0入0汤圆

发表于 2019-10-29 13:47:42 | 显示全部楼层
maoxue121225 发表于 2017-9-5 09:47
你那一定数量是4096吗? tcp_recved是不是这个问题  窗口值的问题 去搜索老衲五木的博客吧。 ...

看了下,这个博客确实不错,感谢推荐,慢慢研读

出0入0汤圆

发表于 2019-10-29 16:34:59 | 显示全部楼层
刚刚看了下8楼推荐的博客,2.0.3及以下版本都有个BUG,楼主可以修复试试看能否解决
这个链接:LwIP BUG之TCP连接丢失

出0入0汤圆

发表于 2020-7-1 12:07:24 | 显示全部楼层
你好楼主  你用LwIP是那个版本的   我也出线了同样的问题,服务器发送一段时间数据后就死机

出0入0汤圆

 楼主| 发表于 2020-7-2 14:54:36 | 显示全部楼层
李嘉辉 发表于 2020-7-1 12:07
你好楼主  你用LwIP是那个版本的   我也出线了同样的问题,服务器发送一段时间数据后就死机 ...

1.4.0 后面测试解决发现,直接调用tcp_send函数发送即可,但接收时要注意pbuff处理

出0入0汤圆

发表于 2020-7-2 14:56:21 | 显示全部楼层
mii 发表于 2020-7-2 14:54
1.4.0 后面测试解决发现,直接调用tcp_send函数发送即可,但接收时要注意pbuff处理 ...

抓包出现这种情况,然后服务器就停止发送数据了

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

 楼主| 发表于 2020-7-2 15:00:42 | 显示全部楼层
李嘉辉 发表于 2020-7-2 14:56
抓包出现这种情况,然后服务器就停止发送数据了

你先做单向测试,一是让服务器定时发送数据测试,二是服务器接收测试。你这样才能确定问题。看看是出现在发送还是接收函数。
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-3-29 10:38

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表