xivisi 发表于 2016-3-23 23:38:34

[2016-04-12已解决,芯片BUG]Lwip RAW模式TCP发送数据阻塞

本帖最后由 xivisi 于 2016-4-12 16:18 编辑

2016-04-12:
终于发现问题所在,使用的处理器有缺陷,外置SDRAM居然不允许ARM核和DMA同时访问(仅内置SRAM支持,日了狗了),芯片缺陷手册居然没有对应描述。调整软件构架、精简内存占用,已解决。

2016-04-07:
昨晚继续进行测试,刚刚发现连接断开,经查,PCB出现了死循环:



、==================================================================

2016-04-06:
      仅协议栈和协议栈相关层代码的情况下:回环、单发、单收均无问题,一加上同事的应用层代码,就出现之前的问题(我的代码几乎没有任何修改)。目前只能判定,同事的代码有问题;
      
===========================================================================================================

目前在调一个 LWIP RAW TCP发送代码,单词发送的数据量比较大,目前48KB;

目前故障现象:发送成功一个包之后,发现该pcb的发送缓存一直满,再也不能发送数据,求助

以下是发送函数的代码:
<i>err_t app_net_connect_send(connect_t *connect)
{
      err_t                                 result;
      connect_txdata_t      *txdata;
      uint32_t                        buf_len, ipc_len, tx_send, write_len;
      struct tcp_pcb               *tpcb;

      txdata = connect->tx_work;      //获取正在发送的数据块
      if(txdata == NULL)                        //如果没有正在发送的数据
      {
                //从等待发送的链表中取出一个数据块
                txdata = (connect_txdata_t*)list_fifo_remove(&connect->tx_ready);
                if(txdata) //如果数据块存在
                {
                        //标记为正在发送的数据块
                        connect->tx_work = txdata;
                        //预先放入完成队列,由TCP接收回调函数处理
                        list_fifo_insert(&connect->tx_finish, &txdata->link);
                }
                else return ERR_OK;
      }
      //取出pcb
      tpcb = connect->pcb;
      assert(tpcb == NULL);
      //计算剩余发送数据的长度
      ipc_len = txdata->length;
      tx_send = txdata->tx_send;
      write_len = ipc_len - tx_send;
      
      if(write_len)
      {
                //使发送数据的长度不超过,TCP发送缓存
                buf_len = tcp_sndbuf(tpcb);
                if(write_len > buf_len) write_len = buf_len;
                //数据加入发送队列
                result = tcp_write(tpcb, &txdata->data, write_len, 1);
                if(result != ERR_OK) return result;
                //强制数据立即发送
                tcp_output(connect->pcb);
                tx_send += write_len;
      }
      //如果所有数据已交给协议栈,认为当前数据块已发送完毕
      if(tx_send == txdata->length) connect->tx_work= NULL;
      //否则只记录发送数据大小
      else txdata->tx_send = tx_send;
      
      return ERR_OK;
}</i>

xivisi 发表于 2016-3-24 09:23:56

更换老版本lwip1.3.2问题依旧,代码操作问题,或配置不正确的可能性比较大
相关配置如下:
#ifndef __LWIPOPTS_H__
#define __LWIPOPTS_H__


#define SYS_LIGHTWEIGHT_PROT                                    0
#define NO_SYS                                                  1
#define NO_SYS_NO_TIMERS                                      1
#define MEM_ALIGNMENT                                                  4
//#define ETH_PAD_SIZE                                                        2

#define MEM_LIBC_MALLOC                                       1
#if !MEM_LIBC_MALLOC
        #define MEM_USE_POOLS                                                0
        #if MEM_USE_POOLS
                #define MEM_USE_POOLS_TRY_BIGGER_POOL        0
                #define MEM_USE_CUSTOM_POOLS                         0
                #define MEMP_USE_CUSTOM_POOLS                        0
                #define MEMP_SEPARATE_POOLS                                0
        #else
                #define MEM_SIZE                        (4*1024*1024)
        #endif
#endif

#define MEMP_NUM_PBUF                 4096
#define MEMP_NUM_UDP_PCB              4
#define MEMP_NUM_SYS_TIMEOUT      300

#define PBUF_POOL_SIZE                  512
#define PBUF_POOL_BUFSIZE               1536

#define LWIP_RAW                        1
#define LWIP_NETIF_HOSTNAME             1
#define LWIP_BROADCAST_PING             1

#define LWIP_TCP                        1
#define MEMP_NUM_TCP_PCB              32
#define MEMP_NUM_TCP_PCB_LISTEN         2
#define MEMP_NUM_TCP_SEG              (TCP_SND_QUEUELEN+64)
#define TCP_TTL                       255
#define TCP_QUEUE_OOSEQ                 0
#define TCP_MSS                       (1460)
#define TCP_SND_BUF                     (8*TCP_MSS)
#define TCP_SND_QUEUELEN             ((16*(TCP_SND_BUF)+(TCP_MSS-1))/(TCP_MSS))

#define LWIP_ICMP                   1
#define LWIP_DHCP                       0

#define LWIP_UDP                              1
#define UDP_TTL                       255

#define LWIP_SOCKET               0
#define LWIP_NETCONN                0
#define LWIP_STATS                  0
#define LINK_STATS                  0
#define LWIP_STATS_DISPLAY          0

#define LWIP_PROVIDE_ERRNO                         1
#define LWIP_NETIF_LINK_CALLBACK    0
#define LWIP_NETCONN                0
#define LWIP_SOCKET               0

#include <stdlib.h>

#define LWIP_DEBUG                        0
#define TCP_OUTPUT_DEBUG         LWIP_DBG_OFF

#endif /* __LWIPOPTS_H__ */

xivisi 发表于 2016-3-24 11:48:33

自己顶                           

xivisi 发表于 2016-3-29 11:20:18

回环模式:接收时拷贝到接收队列,在大循环中调用楼主位发送函数;测试发送 600K的文件,无问题;

但是取消回环模式,加上应用代码(确认应用数据已加入发送队列),立马歇菜。

xivisi 发表于 2016-3-29 12:56:11

更改回环模式:只要接收到任意数据,则持续不断向外每次发送100K数据(相对于发送缓冲,对应于connect->tx_ready这个fifo队列),测试无问题……

myxiaonia 发表于 2016-3-29 14:04:22

不用tcp_output试试,因为raw模式下延时到了自己会发送数据,而tcp_output很可能在协议栈的回调函数里操作协议栈的流程,我认为很可能就会破坏协议的完整性

xivisi 发表于 2016-3-29 14:10:57

myxiaonia 发表于 2016-3-29 14:04
不用tcp_output试试,因为raw模式下延时到了自己会发送数据,而tcp_output很可能在协议栈的回调函数里操作 ...

我不是在回调函数里面调用 tcp_output

xivisi 发表于 2016-3-29 14:28:03

本帖最后由 xivisi 于 2016-3-29 14:30 编辑




抓包,传输终止的地方,没看出异常……

LWIP:192.168.1.115
PC        : 192.168.1.107

xivisi 发表于 2016-3-29 14:42:44

修改发送缓冲大小、增大对应的pbuf数量,目前不阻塞于发送函数tcp_write;
但是,在发送完成函数中(由tcp_sent指定的回调函数),并没有收到对应数量的应答。LWIP 未收到ACK响应?

myxiaonia 发表于 2016-3-29 15:31:23

xivisi 发表于 2016-3-29 14:10
我不是在回调函数里面调用 tcp_output

你以上所发的函数不是发送回调函数吗 这个函数是在什么情况下调用的

xivisi 发表于 2016-3-29 15:34:49

本帖最后由 xivisi 于 2016-3-29 15:36 编辑

myxiaonia 发表于 2016-3-29 15:31
你以上所发的函数不是发送回调函数吗 这个函数是在什么情况下调用的

发送回调函数是由tcp_sent(pcb, app_net_connect_sent);设置的,我的
回调函数是:app_net_connect_sent;
发送函数是:app_net_connect_send,这个很明显是其他任务产生的数据进,在大循环了调用(与 sys_check_timeouts() 平齐);

err_t app_net_connect_sent(void *arg, struct tcp_pcb *tpcb, u16_t len)
{
        uint32_t                        tx_sent;
        connect_t                        *connect;
        connect_txdata_t        *txdata;
        error_t                                result;
       
        connect = (struct connect_t *)arg;
        assert(connect == NULL);
       
        txdata = (connect_txdata_t*)list_fifo_get_first(&connect->tx_finish);
        assert(txdata == NULL);
       
        tx_sent = txdata->tx_sent;
        tx_sent += len;
        while(tx_sent >= txdata->length)
        {
                result = app_ipc_net_tx_finish(connect->id, txdata->data, txdata->length, tx_sent);
                if(result == ERR_NORMAL)
                {
                        list_fifo_remove(&connect->tx_finish);
                        list_single_insert_first(&net.list_free, &txdata->link);
                }
                tx_sent -= txdata->length;
                txdata = (connect_txdata_t*)list_fifo_get_first(&connect->tx_finish);
        }
       
        if(txdata) txdata->tx_sent = tx_sent;

        return ERR_OK;
}

myxiaonia 发表于 2016-3-29 15:44:19

app_net_connect_sent只有list操作吗我没有看到真实的发送操作

一般lwip的raw流程,都是要在lwip协议里通过回调函数的形式操作的,你可以看看rtt的lwip的netio程序,那里就是完全的此类做法

xivisi 发表于 2016-3-29 16:06:08

本帖最后由 xivisi 于 2016-3-29 16:09 编辑

myxiaonia 发表于 2016-3-29 15:44
app_net_connect_sent只有list操作吗我没有看到真实的发送操作

一般lwip的raw流程,都是要在lwip协议里 ...

如果像你说的那样,那发送数据的回调函数怎么触发???
操作队列的目的是,保证发送的数据被对方接收到之后,释放对应的内存。

xivisi 发表于 2016-3-30 10:41:38

本帖最后由 xivisi 于 2016-3-30 10:47 编辑

myxiaonia 发表于 2016-3-29 15:44
app_net_connect_sent只有list操作吗我没有看到真实的发送操作

一般lwip的raw流程,都是要在lwip协议里 ...

死马当活马医,更改了操作模式,依然有些问题;
并且速度暴降到原来的1/10

xivisi 发表于 2016-3-30 12:33:19

本帖最后由 xivisi 于 2016-3-30 12:34 编辑

抓取到一个错误,但是可恢复:

myxiaonia 发表于 2016-3-30 13:10:29

xivisi 发表于 2016-3-29 16:06
如果像你说的那样,那发送数据的回调函数怎么触发???
操作队列的目的是,保证发送的数据被对方接收到 ...

lwip有个帮助文件,rawapi.txt 貌似是这个

发送回调函数什么时候调用,那里有讲解,一般会在发送缓冲区有空的时候,也就是收到对方确认应答后自动回调
也可以调用tcp_write发送,不过要保证协议完整性,协议栈的函数基本上都是不可重入的你得有这个前提
裸机模式下在中断中调用就必须小心,带os的话通过api把此函数调用推入到lwip线程执行

xivisi 发表于 2016-3-30 13:17:43

本帖最后由 xivisi 于 2016-3-30 13:21 编辑

myxiaonia 发表于 2016-3-30 13:10
lwip有个帮助文件,rawapi.txt 貌似是这个

发送回调函数什么时候调用,那里有讲解,一般会在发送缓冲区 ...

4楼已说明,在主程序大循环调用。

对于接收数据也是在大循环里调用,没有使用任何中断,没有使用操作系统。

回调函数仅处理已发送数据,这样实现异步逻辑,加快发送速度。非回调函数调用 tcp_write 需要调用tcp_output实现立即发送。

xivisi 发表于 2016-3-31 09:04:06

调整配置选项,TCP层无用。后来找默认配置里面,IP层发送相关配置,成功。昨晚跑了一晚上OK!

xivisi 发表于 2016-4-6 17:36:45

问题,又出现了。。。。。。。

xivisi 发表于 2016-4-7 09:53:59

抓到BUG一只

xivisi 发表于 2016-4-12 16:19:05

已解决         

goldtiny 发表于 2019-9-3 10:42:57

请问是什么BUG,如何解决,我也遇到了和你一样的现象
页: [1]
查看完整版本: [2016-04-12已解决,芯片BUG]Lwip RAW模式TCP发送数据阻塞