OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 14117|回复: 15

STM32F407 裸跑LWIP 使用tcp_write发送大量数据时,数据丢失!!跪求大神解答!!

[复制链接]

1

主题

3

帖子

0

精华

新手入门

积分
18
金钱
18
注册时间
2018-8-27
在线时间
2 小时
发表于 2018-8-27 18:50:30 | 显示全部楼层 |阅读模式
1金钱
本帖最后由 zhanglang 于 2018-8-28 00:41 编辑

一、硬件环境说明
采用STM32F407ZET6裸跑LWIP,使用CUBEMX生成相关配置

二、项目说明
这是一个串口与网络透传的项目,实现接受串口数据,转发到指定的IP地址。希望能达到,单包传输2K的数据量。

三、问题描述
问题1:MCU发送大量数据到指定IP地址时,出现了数据丢失
问题详细说明:
采用tcp_write()函数发送数据时,发送1K左右的数据量,没有问题,但是当数据量达到2K时候,偶尔会发生数据丢失。
通过串口调试打印得知,在大数据量发送时,tcp_write()函数,产生 ERR_MEM 错误,导致部分数据没有发送成功,使用CubeMX多次尝试重新配置各个可能的参数,问题还是没有得到解决。由于打开 LWIP Debug 选项,在发送2K数据量出现数据丢失时,LWIP同时输出此调试信息。
mem_malloc: could not allocate 1472 bytes
tcp_write : could not allocate memory for pbuf copy size 856

发送部分代码:
[mw_shl_code=cpp,true]void tcp_server_senddata(struct tcp_pcb *tpcb, struct tcp_server_struct *es)
{

        struct pbuf *ptr;
        u16 plen;
        err_t wr_err = ERR_OK;

        while ((wr_err == ERR_OK) && (es->p) && (es->p->len <= tcp_sndbuf(tpcb)))
        {
                printf("tcp_sndbuf=%d es->p->len=%d\r\n", tcp_sndbuf(tpcb), es->p->len);
                ptr = es->p;
                wr_err = tcp_write(tpcb, ptr->payload, ptr->len, 1);
                printf("wr_err=%d\r\n", wr_err);
                if (wr_err == ERR_OK)
                {
                        printf("wr_err==ok\r\n");
                        plen = ptr->len;
                        es->p = ptr->next;
                        if (es->p)
                                pbuf_ref(es->p);
                        pbuf_free(ptr);
                        tcp_recved(tpcb, plen);
                }
                else if (wr_err == ERR_MEM)
                        es->p = ptr;
                tcp_output(tpcb);
        }
//        tcp_output(tpcb);
}[/mw_shl_code]

问题2:在接收数据时,超过1400Byte将丢失
问题详细说明:
经过排查,是由于 TCP_MSS (Maximum Segment Size) 设置为1400所以超过的数据将丢失,我的目标是实现单包传输2KB数据量,但是这个参数最大只能设置为1400。其他可能的参数也尝试配置过,但是还是没有解决问题!

接受部分代码:
[mw_shl_code=cpp,true]err_t tcp_t4_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)
{
        err_t ret_err;
        u32 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(n4_to_u4.recvBuf, 0, NU_RECV_BUF_SIZE);  //数据接收缓冲区清零
                        for (q = p; q != NULL; q = q->next)  //遍历完整个pbuf链表
                        {
                                if (q->len > (NU_RECV_BUF_SIZE - data_len))
                                        memcpy(n4_to_u4.recvBuf + data_len, q->payload,
                                                        (NU_RECV_BUF_SIZE - data_len));                //拷贝数据
                                else
                                        memcpy(n4_to_u4.recvBuf + data_len, q->payload, q->len);
                                data_len += q->len;
                                if (data_len > NU_RECV_BUF_SIZE)
                                        break; //超出TCP客户端接收数组,跳出
                        }

                        extern u32 ntouCnt;                        //缓冲区计数器
                        extern u8 ntouBuf[2048];        //缓冲区

                        u8 * tempBuf = &ntouBuf[ntouCnt];
                        if (ntouCnt + data_len < sizeof(ntouBuf))
                        {
                                ntouCnt += data_len;
                                //接收到的数据拷贝到系统发送缓冲区中
                                memcpy(tempBuf, n4_to_u4.recvBuf, data_len);
                                printf("reciveData=%d,sendData=%d\r\n", data_len, ntouCnt);
                        }

                        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;
}[/mw_shl_code]

LWIP CubeMX配置:


360截图17290504658069.JPG
360截图18490930405459.JPG
正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

57

主题

1680

帖子

3

精华

资深版主

Rank: 8Rank: 8

积分
4306
金钱
4306
注册时间
2018-6-30
在线时间
808 小时
发表于 2018-8-27 19:00:03 | 显示全部楼层
回复

使用道具 举报

558

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
164867
金钱
164867
注册时间
2010-12-1
在线时间
2099 小时
发表于 2018-8-28 01:37:00 | 显示全部楼层
大量数据发送,丢失很正常的。自己加协议保证吧。
回复

使用道具 举报

1

主题

3

帖子

0

精华

新手入门

积分
18
金钱
18
注册时间
2018-8-27
在线时间
2 小时
 楼主| 发表于 2018-8-28 12:12:32 | 显示全部楼层
正点原子 发表于 2018-8-28 01:37
大量数据发送,丢失很正常的。自己加协议保证吧。

也就2K数据,而且项目要求是单包发送2K数据量??
数据量太多,LWIP报了这个错误,难道LWIP不支持单包数据量过大吗?
mem_malloc: could not allocate 1472 bytes
tcp_write : could not allocate memory for pbuf copy size 856
回复

使用道具 举报

558

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
164867
金钱
164867
注册时间
2010-12-1
在线时间
2099 小时
发表于 2018-8-29 01:43:50 | 显示全部楼层
zhanglang 发表于 2018-8-28 12:12
也就2K数据,而且项目要求是单包发送2K数据量??
数据量太多,LWIP报了这个错误,难道LWIP不支持单包数 ...

TCP IP最大的包就是1500字节左右。
所以你要发送2K,建议分512一个包,发4次
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复

使用道具 举报

1

主题

33

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
250
金钱
250
注册时间
2015-10-13
在线时间
88 小时
发表于 2018-8-29 10:38:10 | 显示全部楼层
这个肯定要分开发的兄弟,做协议吧
回复

使用道具 举报

1

主题

3

帖子

0

精华

新手入门

积分
18
金钱
18
注册时间
2018-8-27
在线时间
2 小时
 楼主| 发表于 2018-8-29 15:03:53 | 显示全部楼层
多谢原子哥,问题已解决!!!
问题1:无法单包发送超过2K的数据。
问题1原因:经过排查,是由于 MEM_SIZE (Heap Memory Size) 这个参数设置得过小导致的,以前也设置过,但是还是设置得太小了,同时也将最小堆空间加大了,可能堆空间大小也有点关系吧!
问题2:单包无法接受超过2K的数据。
问题2原因:这个是由于自己的数据接收逻辑代码出错导致的,LWIP协议栈一个接收段最大只能是1.5KB左右,但是如果单包数据量超过1.5KB,会自动拆分成多个数据包,一次接收超过2K的数据量是不会导致丢包的。
回复

使用道具 举报

14

主题

33

帖子

0

精华

初级会员

Rank: 2

积分
107
金钱
107
注册时间
2018-6-15
在线时间
32 小时
发表于 2018-9-9 22:36:58 | 显示全部楼层
zhanglang 发表于 2018-8-29 15:03
多谢原子哥,问题已解决!!!
问题1:无法单包发送超过2K的数据。
问题1原因:经过排查,是由于 MEM_SIZ ...

您好,我想请教下我现在也有接包的问题,超过1.5k就会丢失,请问您是怎么解决的啊,怎样拆分多个数据包接收呢
回复

使用道具 举报

17

主题

58

帖子

0

精华

初级会员

Rank: 2

积分
193
金钱
193
注册时间
2019-1-18
在线时间
67 小时
发表于 2019-4-3 16:48:02 | 显示全部楼层
zhanglang 发表于 2018-8-29 15:03
**** 作者被禁止或删除 内容自动屏蔽 ****

同样是1400接收上限,是要在接收函数里更改逻辑,还是怎么操作可以说一下思路吗
回复

使用道具 举报

13

主题

69

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
237
金钱
237
注册时间
2016-6-22
在线时间
50 小时
发表于 2019-4-5 09:23:06 | 显示全部楼层
1.程序mtu默认值1500,除去IP,TCP首部及其他字节外,约1420
2.超过上述长度时,应该拆包发送和接收。
回复

使用道具 举报

17

主题

58

帖子

0

精华

初级会员

Rank: 2

积分
193
金钱
193
注册时间
2019-1-18
在线时间
67 小时
发表于 2019-4-29 09:37:27 | 显示全部楼层
huanghuang 发表于 2019-4-5 09:23
1.程序mtu默认值1500,除去IP,TCP首部及其他字节外,约1420
2.超过上述长度时,应该拆包发送和接收。

请教一下,接收的话,拆包是指在接收回调函数里面修改接收逻辑吗?
回复

使用道具 举报

13

主题

69

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
237
金钱
237
注册时间
2016-6-22
在线时间
50 小时
发表于 2019-5-7 20:28:25 | 显示全部楼层
是的,就是回调函数中读取。
1111.png
回复

使用道具 举报

558

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
164867
金钱
164867
注册时间
2010-12-1
在线时间
2099 小时
发表于 2019-5-8 02:00:06 | 显示全部楼层
网络单包最大就是1.5K字节左右,建议安小包分包,分多次发送。
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复

使用道具 举报

17

主题

58

帖子

0

精华

初级会员

Rank: 2

积分
193
金钱
193
注册时间
2019-1-18
在线时间
67 小时
发表于 2019-5-9 11:55:12 | 显示全部楼层
huanghuang 发表于 2019-5-7 20:28
是的,就是回调函数中读取。

兄弟,再请教一下,你的意思是大数据量的时候,发送方分包发送的数据我这边要接收的话,是要在最底层的LOW_E=LEVEL_INPUT中修改接收逻辑,比如在这个函数中把数据拷贝到一个缓冲数组中存满再做处理?我目前的做法是用的UCOSII+LWIP在tcp_server_thread任务里把接收逻辑改成这样,但是只能收到1-3帧数据。问题出在哪里,如果可以的话帮我看一下我发的求助贴,谢谢了啊http://www.openedv.com/forum.php ... &extra=page%3D1
回复

使用道具 举报

3

主题

23

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
266
金钱
266
注册时间
2019-8-5
在线时间
40 小时
发表于 2020-5-27 12:00:56 | 显示全部楼层
建议数据包发送与接都分包为1024或512,然后收发再进行组装。另回调函数据最好不要做数据处理,用二维数组来接收缓存。
回复

使用道具 举报

0

主题

2

帖子

0

精华

新手入门

积分
11
金钱
11
注册时间
2023-5-6
在线时间
2 小时
发表于 2023-10-30 17:07:20 | 显示全部楼层
正点原子 发表于 2018-8-29 01:43
TCP IP最大的包就是1500字节左右。
所以你要发送2K,建议分512一个包,发4次

发100k是不是也要这样分包
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2024-5-14 05:33

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

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