RT-Thread / rt-thread

RT-Thread is an open source IoT Real-Time Operating System (RTOS).
https://www.rt-thread.io
Apache License 2.0
10.47k stars 5.01k forks source link

at_device在下载大文件时出现数据错误的bug #3600

Open akinggw opened 4 years ago

akinggw commented 4 years ago

我使用AT_device通过http从网站上下载大文件时,发现每次数据都不正常,数据中总是夹杂着大量无用的数据,通过查找,发现rt-thread/components/net/at/at_socket/at_socket.c这个文件中的 static size_t at_recvpkt_get(rt_slist_t rlist, char mem, size_t len) 中链表使用应该是存在问题的,修改后的代码如下: static size_t at_recvpkt_get(rt_slist_t rlist, char mem, size_t len) { rt_slist_t *node = RT_NULL; at_recv_pkt_t pkt = RT_NULL; size_t content_pos = 0, page_pos = 0;

if (rt_slist_isempty(rlist)) { return 0; }

for (node = rt_slist_first(rlist); node; ) { pkt = rt_slist_entry(node, struct at_recv_pkt, list);

page_pos = pkt->bfsz_totle - pkt->bfsz_index;

//rt_kprintf("at_recvpkt_get3:%d %d\n",pkt->bfsz_totle,pkt->bfsz_index);

if (page_pos >= len - content_pos)
{
    memcpy((char *) mem + content_pos, pkt->buff + pkt->bfsz_index, len - content_pos);
    pkt->bfsz_index += len - content_pos;
    if (pkt->bfsz_index == pkt->bfsz_totle)
    {
        at_recvpkt_node_delete(rlist, node);
    }

    content_pos = len - content_pos;
    break;
}
else
{
    memcpy((char *) mem + content_pos, pkt->buff + pkt->bfsz_index, page_pos);
    content_pos += page_pos;

    pkt->bfsz_index += page_pos;
    rt_slist_t *tmpnode = rt_slist_next(node);
    at_recvpkt_node_delete(rlist, node);
    node = tmpnode;
    //node = rt_slist_next(node);
}

} static int at_recvpkt_all_delete(rt_slist_t rlist) 这个函数同样有问题,在关闭socket时,清理接收消息列表时会报异常,导致系统中断,修改如下: static int at_recvpkt_all_delete(rt_slist_t rlist) { at_recv_pkt_t pkt = RT_NULL; rt_slist_t *node = RT_NULL;

//rt_kprintf("at_recvpkt_all_delete1.\n");

if (rt_slist_isempty(rlist)) { return 0; }

rt_kprintf("at_recvpkt_all_delete2.\n");

for(node = rt_slist_first(rlist); node; ) { rt_slist_t *tmpnode = rt_slist_next(node); at_recvpkt_node_delete(rlist,node); node = tmpnode; }

//rt_kprintf("at_recvpkt_all_delete3.\n");

return 0; } 通过测试,现在下载数据一切正常。

Lawlieta commented 4 years ago

你这边改动有几个问题想和你确定一下:

  1. content_pos 返回值问题

    if (page_pos >= len - content_pos)
    {
    memcpy((char *) mem + content_pos, pkt->buff + pkt->bfsz_index, len - content_pos);
    pkt->bfsz_index += len - content_pos;
    if (pkt->bfsz_index == pkt->bfsz_totle)
    {
        at_recvpkt_node_delete(rlist, node);
    }
    
    content_pos = len - content_pos;
    break;
    }

    如果 at_recvpkt_get 函数中走这段数据判断这说明当前缓冲区有足够长度数据,则直接从缓冲区中拿数据,这里的 content_pos 应该是获取的长度,理应是 content_pos 返回长度 len,为什么你这里修改为 content_pos = len - content_pos; ?

  2. 你把链表循环修改为 for (node = rt_slist_first(rlist); node; ) 方式,然后在循环判断中修改为如下判断

    else
    {
    memcpy((char *) mem + content_pos, pkt->buff + pkt->bfsz_index, page_pos);
    content_pos += page_pos;
    
    pkt->bfsz_index += page_pos;
    rt_slist_t *tmpnode = rt_slist_next(node);
    at_recvpkt_node_delete(rlist, node);
    node = tmpnode;
    //node = rt_slist_next(node);
    }

    实际效果和之前 for (node = rt_slist_first(rlist); node; node = rt_slist_next(node)) 效果一致,因为如果缓冲区中有实际数据的话,会直接走前面判断,直接 break 退出循环,不会再进行 rt_slist_next(node) 循环,所以详细描述一下你现在改动的目的?我这边分析你改动之后的数据返回值可能会出现问题,你再确定一下。

akinggw commented 4 years ago

我只是怀疑链表的删除可能有问题,比如stl的list,如果像原来代码的写法是有问题的,一般正确的写法像下面这样:

for (it = ListNumber->begin(); it != ListNumber->end(); ) { min = (*it) - 1000; if (min < 0 ) { it =ListNumber->erase(it); //ListNumber->erase(it); 错误 //ListNumber->erase(it++);正确 }else{ it++; }

} 所以我怀疑链表的写法有问题。

akinggw commented 4 years ago

我在下载一个大文件时,数据大小都是对得上的,但数据不正确,因为我每次下载完成后都会进行数据完整性效验,就发现了上述这个问题。

akinggw commented 4 years ago

而且我发现这个链表使用的地方非常的多,所以就提出来了,特别是在链表摘除的时候,如果有问题的话,应该影响是很大的。

Lawlieta commented 4 years ago

这种链表处理方式非常常见呀,在RT-Thread 组件和软件包很多地方有用到,比如 at_device 软件包,lssdp 软件包等,写法也是上面类似的写法没有问题,下面是 at_deivce 软件包中使用:

QQ截图20200518094514

你是否可以提供一下具体问题复现的示例,我们可以试着帮你复现确定一下问题?

akinggw commented 4 years ago

https://gitee.com/akinggw/MIVMS/tree/master/nanopi 这是我改的,不过是基于f1c100s的。https://gitee.com/akinggw/MIVMS/blob/master/nanopi/rt-thread/applications/http_ota.c这个文件,用于固件升级,现在已经没啥问题了,但感觉下载还是有点怪啊。对rt-thread不熟,是能先暂时这样了

ztlchina commented 2 years ago

哎~~ 我也遇到这个问题。 非常头疼。 http_ota 使用EG25 下载的时候在中间丢了一个两个数。 不知道是串口的问题。 还是at client的问题。 串口缓存已经放大了,还是不行。