Lwip pbuf分析

在BSD中用mbuf结构体来管理网络上来的各种数据包,同样lwip中也有一个类似的结构体pbuf用来管理数据包。Pbuf结构体定义如下:

struct pbuf {  

        struct pbuf *next;                                                                                                                                          

        void *payload;   

        u16_t tot_len;    

        u16_t len;  

        u8_t /*pbuf_type*/  type;                                                                                      

        u8_t flags;  

        u16_t ref;  

} ;  

next: 指向下一个pbuf,在数据很大时,需要多个pbuf结构体管理,这里就是通过next指针将这些next链接起来。

payload:指向实际载荷数据的起始地址。

tot_len: 是当前pbuf数据加上next之后所有pbuf数据之和。Pbuf链表的第一个pbuf的tot_len就是所有pbuf数据的长度,最后一个pbuf的tot_len等于len。

len: 是当前pbuf的数据长度。

type: 此字段表示pbuf的类型,有四种类型:PBUF_RAM、PBUF_ROM、PBUF_REF和PBUF_POOL。

ref:此字段初始化为1,当其他pbuf->next指向自己时ref加1,在释放的时候,ref大于1的不能删除。

(我感觉红色字体是有问题的,pbuf申请时候不就是一个链式结构么,next->next...难道第一个是1,后面的

就是2,3,4等等累加?当然不是了,如果这样的话,你想想怎么去free,所以一次申请的一个pbuf链,里面

的ref字段当然都是1了。当我们想把两个pbuf拼接的时候,看你想怎么操作,可以直接拼接,也可以先用

pbuf_ref把后面的ref+1,然后再拼接,但是释放的时候,就要释放两次)


pbuf类型
PBUF_RAM

此种类型的pbuf的分配代码片段如下:

case PBUF_RAM:  

    p = (struct pbuf*)mem_malloc(LWIP_MEM_ALIGN_SIZE(SIZEOF_STRUCT_PBUF + offset) + LWIP_MEM_ALIGN_SIZE(length));                        

    /* Set up internal structure of the pbuf. */   

    p->payload =  LWIP_MEM_ALIGN((void *)((u8_t *)p + SIZEOF_STRUCT_PBUF + offset));  

    p->len =  p->tot_len =  length;  

    p->next =  NULL;  

    p->type =  type;  

    break;  

PBUF_RAM类型的pbuf是调用mem_malloc函数从内存堆分配得到的,分配的大小由三部分组成:数据存储空间length、pbuf管理结构体空间SIZEOF_STRUCT_PBUF和存储协议栈头的offset。


分配内存成功之后,就是对pbuf管理结构体的初始化。Pbuf管理结构体位于分配的堆内存的开始,接着的存储协议头的offset空间,最后才是存储数据的空间。

此种类型的pbuf内存布局如下:

Lwip pbuf分析_第1张图片

PBUF_ROM
PBUF_REF
上述两种类型的pbuf相似,在pbuf_alloc函数中申请内存时共用代码:

/* pbuf references existing (non-volatile static constant) ROM payload? */                                                                          

case PBUF_ROM:  

/* pbuf references existing (externally allocated) RAM payload? */   

case PBUF_REF:  

        /* only allocate memory for the pbuf structure */   

        p = (struct pbuf *)memp_malloc(MEMP_PBUF);  

        /* caller must set this field properly, afterwards */   

        p->payload =  NULL;  

        p->len =  p->tot_len =  length;  

        p->next =  NULL;  

        p->type =  type;  

        break;  

代码调用memp_malloc从内存池分配MEMP_PBUF类型的内存池,仅仅分配pbuf结构体大内存,指向存储数据空间的payload指针置位NULL,此值由调用者设置为另外的一片内存空间。


PBUF_POOL
PBUF_POOL类型的pbuf是调用memp_malloc函数从内存池中分配内存的。

case PBUF_POOL:  

        p = (struct pbuf *)memp_malloc(MEMP_PBUF_POOL);  

        p->type =  type;  

        p->next =  NULL;  

        p->payload =  LWIP_MEM_ALIGN((void *)((u8_t *)p + (SIZEOF_STRUCT_PBUF + offset)));  

        p->tot_len =  length;  

        p->len=LWIP_MIN(length,PBUF_POOL_BUFSIZE_ALIGNED-LWIP_MEM_ALIGN_SIZE(offset));                                   

从上面代码片段可以看出PBUF_POOL类型的pbuf和PBUF_RAM类型布局相似,但有一点不同的是:PBUF_POOL类型pbuf是从MEMP_PBUF_POOL内存的内存池中分配内存的,每种类型的内存池大小时固定的,如果存储数据和协议头所需要的空间大于此种类型内存池大小,则需要分配多个此种类型的内存池,并将这些内存池通过pbuf->next指针连接起来。而PBUF_RAM类型的pbuf是从内存堆中分配内存,之用申请的内存空间有剩余的连续空闲空间满足要求,则一次分配成功。


r = p;   

rem_len = length -  p->len ;  

while (rem_len > 0) {  

        q = (struct pbuf *)memp_malloc(MEMP_PBUF_POOL);  

        q->type =  type;  

        q->flags =  0;  

        q->next =  NULL;  

        r->next =  q;  

        q->tot_len =  (u16_t)rem_len;  

        q->len =  LWIP_MIN((u16_t)rem_len, PBUF_POOL_BUFSIZE_ALIGNED);                                                                                   

        q->payload =  (void *)((u8_t *)q + SIZEOF_STRUCT_PBUF);  

        q->ref =  1;  

        rem_len -=  q->len ;  

        r = q;  

}

上面代码首先计算需要的内存空间length和第一个pbuf指针p指向的可以空间len之差rem_len。如果rem_len大于0,说明还需要分配此种类型的内存池,于是进入while循环之中。在while循环中会分配更多的PBUF_POOL类型的pbuf直到能够存储下length大小的数据,并将这些pbuf连接起来。


需要注意的一点是除了第一个pbuf在pbuf后面需要留offset大小空间存储协议头之外,其它的pbuf中是不需要的。

PBUF_POOL类型的内存池MEMP_PBUF_POOL定义如下:

LWIP_PBUF_MEMPOOL(PBUF_POOL, PBUF_POOL_SIZE,         

PBUF_POOL_BUFSIZE, "PBUF_POOL")

其大小为PBUF_POOL_BUFSIZE宏:

LWIP_MEM_ALIGN_SIZE(TCP_MSS+40+PBUF_LINK_HLEN)

包括TCP报文段最大长度MSS,40字节的tcp+IP头长度和链接层长度PBUF_LINK_HLEN


最终的PBUF_POOL类型的pbuf内存布局如下:

Lwip pbuf分析_第2张图片
Pbuf释放
前面分析pbuf类型的时候讲了分配内存函数pbuf_alloc对于四种类型的pbuf各自的代码,对于PBUF_RAM类型的pbuf是从调用mem_malloc内存堆中内存,其它三种是调用函数memp_malloc从内存池中分配内存。相对应的,PBUF_RAM类型pbuf的释放就需要调用mem_free函数将内存释放回内存堆,其它三种就是memp_free函数将内存释放回相应类型的内存池了。


下面分析pbuf_free函数代码片段:

while (p !=  NULL) {  

        ref = --(p->ref );  

        if (ref == 0) {   

                q = p->next ;  

                type = p->type ;  

                if (type == PBUF_POOL) {  

                        memp_free(MEMP_PBUF_POOL, p);  

                } else if (type == PBUF_ROM || type == PBUF_REF) {                                                                                                               

                        memp_free(MEMP_PBUF, p);     

                } else {  

                    mem_free(p);  

                }  

 

                p = q;  

        } else {  

                p = NULL;  

        }  

}   


pbuf的ref成员初始化时设置为1,每当有其它pbuf的next指针指向自己时ref值加1。只有当ref值为1时,表示没有其它pbuf指向自己时,才可以释放此pbuf。这里首先将pbuf的ref值减1,并赋值给变量ref。如果ref值为0说明此pbuf没有被其它的pbuf引用,这也是此pbuf能够释放的前提。如果ref值不为0,说明此pbuf不能释放,设置p为NULL,则直接退出此while循环。


对于ref等于0的情况,首先获取p->next指向的pbuf并赋值给指针q,然后根据此pbuf的type分别调用不同的释放函数。

◆PBUF_POOL类型

调用memp_free函数将p的内存空间返回给MEMP_PBUF_POOL类型的内存池。

◆PBUF_ROM和PBUF_REF类型

调用memp_free函数将p的内存空间返回给MEMP_PBUF类型的内存池

◆PBUF_RAM类型

调用mem_free将p的内存空间返回给内存堆。


内存堆的释放和内存池的释放这里不分析。这里看下ref值不同,对于pbuf链表在pbuf_free函数执行后产生的不同结果。在pbuf_free函数的前面有一段注释:

* @note the reference counter of a pbuf equals the number of pointers                                                                       

 * that refer to the pbuf (or into the pbuf).  

 *  

 * @internal examples:  

 *  

 * Assuming existing chains a->b->c with the following reference  

 * counts, calling pbuf_free(a) results in:  

 *  

 * 1->2->3 becomes ...1->3  

 * 3->3->3 becomes 2->3->3  

 * 1->1->2 becomes ......1  

 * 2->1->1 becomes 1->1->1  

 * 1->1->1 becomes .......  


假设有一个链表由a、b和c三个pbuf按顺序链接起来的,对于这三个pbuf的ref值在如下几种情况下调用pbuf_free(a)所产生的结果如下:


Lwip pbuf分析_第3张图片


--------------------- 
作者:weijitao 
来源:CSDN 
原文:https://blog.csdn.net/weijitao/article/details/53493529 
版权声明:本文为博主原创文章,转载请附上博文链接!

 

1、pbuf结构体

struct pbuf {
  struct pbuf *next;
  void *payload;
  u16_t tot_len;
  u16_t len;
  u8_t /*pbuf_type*/ type;
  u8_t flags;
  u16_t ref;
};

typedef enum {
  PBUF_TRANSPORT,
  PBUF_IP,
  PBUF_LINK,
  PBUF_RAW
} pbuf_layer;

typedef enum {
  PBUF_RAM, /* pbuf data is stored in RAM */
  PBUF_ROM, /* pbuf data is stored in ROM */
  PBUF_REF, /* pbuf comes from the pbuf pool */
  PBUF_POOL /* pbuf payload refers to RAM */
} pbuf_type;



2、宏声明

//TCP/IP协议各层头部字节数
#define PBUF_TRANSPORT_HLEN 20
#define PBUF_IP_HLEN        20
#define PBUF_LINK_HLEN      (14 + ETH_PAD_SIZE)
#define ETH_PAD_SIZE         0

//内存字节对齐
#define SIZEOF_STRUCT_PBUF        LWIP_MEM_ALIGN_SIZE(sizeof(struct pbuf))
#define PBUF_POOL_BUFSIZE_ALIGNED LWIP_MEM_ALIGN_SIZE(PBUF_POOL_BUFSIZE)

//PBUF_POOL类型的内存池个数
#define PBUF_POOL_SIZE          20
//一个PBUF_POOL类型的内存池容量
#define PBUF_POOL_BUFSIZE       500
//PBUF类型的内存池个数,该类型内存池容量为sizeof(struct pbuf)
#define MEMP_NUM_PBUF           100

#define LWIP_PBUF_MEMPOOL(name, num, payload, desc) 
LWIP_MEMPOOL(name, num, MEMP_ALIGN_SIZE(sizeof(struct pbuf)) + MEMP_ALIGN_SIZE(payload)), desc)
LWIP_PBUF_MEMPOOL(PBUF, MEMP_NUM_PBUF,   0,  "PBUF_REF/ROM")
LWIP_PBUF_MEMPOOL(PBUF_POOL, PBUF_POOL_SIZE,   PBUF_POOL_BUFSIZE,   "PBUF_POOL")


3、pbuf_alloc

struct pbuf *pbuf_alloc(pbuf_layer layer, u16_t length, pbuf_type type)
{
  struct pbuf *p, *q, *r;
  u16_t offset;
  s32_t rem_len; /* remaining length */

  /* determine header offset */
  switch (layer) {
  case PBUF_TRANSPORT:
    /* add room for transport (often TCP) layer header */
    offset = PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN;
    break;
  case PBUF_IP:
    /* add room for IP layer header */
    offset = PBUF_LINK_HLEN + PBUF_IP_HLEN;
    break;
  case PBUF_LINK:
    /* add room for link layer header */
    offset = PBUF_LINK_HLEN;
    break;
  case PBUF_RAW:
    offset = 0;
    break;
  default:
    LWIP_ASSERT("pbuf_alloc: bad pbuf layer", 0);
    return NULL;
  }

  switch (type) {

  //申请PBUF_POOL类型的内存池
  case PBUF_POOL:    
    /* allocate head of pbuf chain into p */
    p = (struct pbuf *)memp_malloc(MEMP_PBUF_POOL);

    if (p == NULL) {
      PBUF_POOL_IS_EMPTY();
      return NULL;
    }
    p->type = type;
    p->next = NULL;

    /* make the payload pointer point 'offset' bytes into pbuf data memory */
    //pbuf链表的第一个pbuf需填写协议头部字节
    p->payload = LWIP_MEM_ALIGN((void *)((u8_t *)p + (SIZEOF_STRUCT_PBUF + offset)));
    p->tot_len = length;
    // 判断一个pbuf_pool能否装下申请字节数
    p->len = LWIP_MIN(length, PBUF_POOL_BUFSIZE_ALIGNED - LWIP_MEM_ALIGN_SIZE(offset));

    /* set reference count (needed here in case we fail) */
    p->ref = 1;

    /* remember first pbuf for linkage in next iteration */
    r = p;
    /* remaining length to be allocated */
    rem_len = length - p->len;

    /* any remaining pbufs to be allocated? */
    //若一个pbuf_pool装不下申请字节数,则生成pbuf链表
    while (rem_len > 0) {
      q = (struct pbuf *)memp_malloc(MEMP_PBUF_POOL);

      if (q == NULL) {
        PBUF_POOL_IS_EMPTY();
        /* free chain so far allocated */
        pbuf_free(p);
        /* bail out unsuccesfully */
        return NULL;
      }
      q->type = type;
      q->flags = 0;
      q->next = NULL;

      /* make previous pbuf point to this pbuf */
        //连接pbuf
      r->next = q;

      /* set total length of this pbuf and next in chain */
      q->tot_len = (u16_t)rem_len;
      /* this pbuf length is pool size, unless smaller sized tail */
      q->len = LWIP_MIN((u16_t)rem_len, PBUF_POOL_BUFSIZE_ALIGNED);
      //第二个pbuf不需要装在协议头部字节
      q->payload = (void *)((u8_t *)q + SIZEOF_STRUCT_PBUF);

      q->ref = 1;
      /* calculate remaining length to be allocated */
      rem_len -= q->len;
      /* remember this pbuf for linkage in next iteration */
      r = q;
    }
    /* end of chain */
    /*r->next = NULL;*/
    break;

//采用内存堆函数对pbuf申请
  case PBUF_RAM:
    /* If pbuf is to be allocated in RAM, allocate memory for it. */
    p = (struct pbuf*)mem_malloc(LWIP_MEM_ALIGN_SIZE(SIZEOF_STRUCT_PBUF + offset) + LWIP_MEM_ALIGN_SIZE(length));
    if (p == NULL) {
      return NULL;
    }
    /* Set up internal structure of the pbuf. */
    p->payload = LWIP_MEM_ALIGN((void *)((u8_t *)p + SIZEOF_STRUCT_PBUF + offset));
    p->len = p->tot_len = length;
    p->next = NULL;
    p->type = type;
    break;
  /* pbuf references existing (non-volatile static constant) ROM payload? */
  case PBUF_ROM:

/* pbuf references existing (externally allocated) RAM payload? */
  //申请PBUF类型的内存池
  case PBUF_REF:
    /* only allocate memory for the pbuf structure */
    p = (struct pbuf *)memp_malloc(MEMP_PBUF);
    if (p == NULL) {
      return NULL;
    }
    /* caller must set this field properly, afterwards */
    p->payload = NULL;
    p->len = p->tot_len = length;
    p->next = NULL;
    p->type = type;
    break;
  default:
    LWIP_ASSERT("pbuf_alloc: erroneous type", 0);
    return NULL;
  }
  /* set reference count */
  p->ref = 1;
  /* set flags */
  p->flags = 0;

  return p;
}



4、pbuf_free

u8_t pbuf_free(struct pbuf *p)
{
  u16_t type;
  struct pbuf *q;
  u8_t count;

  if (p == NULL) {
    return 0;
  }

  count = 0;
  /* de-allocate all consecutive pbufs from the head of the chain that
   * obtain a zero reference count after decrementing*/
  while (p != NULL) {
    u16_t ref;
    SYS_ARCH_DECL_PROTECT(old_level);
    /* Since decrementing ref cannot be guaranteed to be a single machine operation
     * we must protect it. We put the new ref into a local variable to prevent
     * further protection. */
    SYS_ARCH_PROTECT(old_level);
    /* all pbufs in a chain are referenced at least once */
    LWIP_ASSERT("pbuf_free: p->ref > 0", p->ref > 0);
    /* decrease reference count (number of pointers to pbuf) */
    ref = --(p->ref);
    SYS_ARCH_UNPROTECT(old_level);

    /* this pbuf is no longer referenced to? */
    //如果该pbuf内的ref为0,则释放该pbuf
    if (ref == 0) {
      /* remember next pbuf in chain for next iteration */
      q = p->next;
      type = p->type;
      {
        /* is this a pbuf from the pool? */
        if (type == PBUF_POOL) {
          memp_free(MEMP_PBUF_POOL, p);
        /* is this a ROM or RAM referencing pbuf? */
        } else if (type == PBUF_ROM || type == PBUF_REF) {
          memp_free(MEMP_PBUF, p);
        /* type == PBUF_RAM */
        } else {
          mem_free(p);
        }
      }
      count++;
      /* proceed to next pbuf */
      p = q;
    /* p->ref > 0, this pbuf is still referenced to */
    /* (and so the remaining pbufs in chain as well) */
    } else {

      /* stop walking through the chain */
      p = NULL;
    }
  }
  PERF_STOP("pbuf_free");
  /* return number of de-allocated pbufs */
  return count;
}


--------------------- 
作者:youk110 
来源:CSDN 
原文:https://blog.csdn.net/youk110/article/details/55100450 
版权声明:本文为博主原创文章,转载请附上博文链接!

 

 

 

好了,有了上面的知识,我们基本就理解了pbuf设计原理。我们首先来说说pbuf为什么设计这样,就是为了兼容各种类型

的应用层数据,比如一个APDU包含RAM中的数据,ROM中的数据等。接下来我们在

具体分析下原子的收发历程里面的具体的收和发是怎么处理的:

物理层接受:

///用于接收数据包的最底层函数
//neitif:网卡结构体指针
//返回值:pbuf数据结构体指针
static struct pbuf * low_level_input(struct netif *netif)
{  
	struct pbuf *p, *q;
	u16_t len;
	int l =0;
	FrameTypeDef frame;
	u8 *buffer;
	p = NULL;
	frame=ETH_Rx_Packet();
	len=frame.length;//得到包大小
	buffer=(u8 *)frame.buffer;//得到包数据地址 
	p=pbuf_alloc(PBUF_RAW,len,PBUF_POOL);//pbufs内存池分配pbuf
	if(p!=NULL)
	{
		for(q=p;q!=NULL;q=q->next)
		{
			memcpy((u8_t*)q->payload,(u8_t*)&buffer[l], q->len);
			l=l+q->len;
		}    
	}
	frame.descriptor->Status=ETH_DMARxDesc_OWN;//设置Rx描述符OWN位,buffer重归ETH DMA 
	if((ETH->DMASRÐ_DMASR_RBUS)!=(u32)RESET)//当Rx Buffer不可用位(RBUS)被设置的时候,重置它.恢复传输
	{ 
		ETH->DMASR=ETH_DMASR_RBUS;//重置ETH DMA RBUS位 
		ETH->DMARPDR=0;//恢复DMA接收
	}
	return p;
}

申请内存:p=pbuf_alloc(PBUF_RAW,len,PBUF_POOL);//pbufs内存池分配pbuf

这里用的PBUF_POOL,而没有用PBUF_RAM,我猜想一个是因为效率问题,一个

是因为碎片问题。

数据复制的时候,必须按着pbuf链去copy,如下操作:

	if(p!=NULL)
	{
		for(q=p;q!=NULL;q=q->next)
		{
			memcpy((u8_t*)q->payload,(u8_t*)&buffer[l], q->len);
			l=l+q->len;
		}    
	}

为什么?因为pbuf申请时候,就是链式结构啊。。。。。。

好,看看上层的回调怎么处理:

		if(p!=NULL)//当处于连接状态并且接收到的数据不为空时
		{
			memset(tcp_client_recvbuf,0,TCP_CLIENT_RX_BUFSIZE);  //数据接收缓冲区清零
			for(q=p;q!=NULL;q=q->next)  //遍历完整个pbuf链表
			{
				//判断要拷贝到TCP_CLIENT_RX_BUFSIZE中的数据是否大于TCP_CLIENT_RX_BUFSIZE的剩余空间,如果大于
				//的话就只拷贝TCP_CLIENT_RX_BUFSIZE中剩余长度的数据,否则的话就拷贝所有的数据
				if(q->len > (TCP_CLIENT_RX_BUFSIZE-data_len)) memcpy(tcp_client_recvbuf+data_len,q->payload,(TCP_CLIENT_RX_BUFSIZE-data_len));//拷贝数据
				else memcpy(tcp_client_recvbuf+data_len,q->payload,q->len);
				data_len += q->len;  	
				if(data_len > TCP_CLIENT_RX_BUFSIZE) break; //超出TCP客户端接收数组,跳出	
			}
			tcp_client_flag|=1<<6;		//标记接收到数据了
 			tcp_recved(tpcb,p->tot_len);//用于获取接收数据,通知LWIP可以获取更多数据
			pbuf_free(p);  	//释放内存
			ret_err=ERR_OK;
		}

从pbuf往外拷贝的时候,毕业也按着链式结构copy,不解释。看看这里的处理,当

if(data_len > TCP_CLIENT_RX_BUFSIZE) break; //超出TCP客户端接收数组,跳出

再看:

下面是:

pbuf_free(p);      //释放内存

即:当pbuf的数据长度超过缓存时候,直接丢弃,处理方式有如下两种思路:

1、缓存为queue模式,长度为大于一个帧长长度,并且和APP解析不共用缓存(当然也可以

共用,主要看APP层设计);

2、调用pbuf_ref(q),pbuf_free(p),然后把q传给全局变量保存起来,后来再去读;这里为什么

优先调用pbuf_ref(q),不解释;

 

发送讲完了,现在讲讲发送:

首先看APP层发送:

		if(tcp_client_flag&(1<<7))	//判断是否有数据要发送 
		{
			es->p=pbuf_alloc(PBUF_TRANSPORT, strlen((char*)tcp_client_sendbuf),PBUF_POOL);	//申请内存 
			pbuf_take(es->p,(char*)tcp_client_sendbuf,strlen((char*)tcp_client_sendbuf));	//将tcp_client_sentbuf[]中的数据拷贝到es->p_tx中
			tcp_client_senddata(tpcb,es);//将tcp_client_sentbuf[]里面复制给pbuf的数据发送出去
			tcp_client_flag&=~(1<<7);	//清除数据发送标志
			if(es->p)pbuf_free(es->p);	//释放内存
		}else if(es->state==ES_TCPCLIENT_CLOSING)
		{ 
 			tcp_client_connection_close(tpcb,es);//关闭TCP连接
		} 

这块没啥说的,就是申请一个pbuf,然后把数据用pbuf_take函数copy进去,调用:

//此函数用来发送数据
void tcp_client_senddata(struct tcp_pcb *tpcb, struct tcp_client_struct * es)
{
	struct pbuf *ptr; 
 	err_t wr_err=ERR_OK;
	while((wr_err==ERR_OK)&&es->p&&(es->p->len<=tcp_sndbuf(tpcb)))
	{
		ptr=es->p;
		wr_err=tcp_write(tpcb,ptr->payload,ptr->len,1); //将要发送的数据加入到发送缓冲队列中
		if(wr_err==ERR_OK)
		{  
			es->p=ptr->next;			//指向下一个pbuf
			if(es->p)pbuf_ref(es->p);	//pbuf的ref加一
			pbuf_free(ptr);				//释放ptr 
		}else if(wr_err==ERR_MEM)es->p=ptr;
		tcp_output(tpcb);		//将发送缓冲队列中的数据立即发送出去
	} 	
}

这块的处理很有意思,历程在接收时候,如果一个pbuf的数据超过了现在缓存可用长度,则直接丢弃,

但是发送时候却没有这么处理,它是一个一个pbuf的Item去tcp_write(链式结构嘛),然后仅仅把这

个发送成功的Item释放掉(因为有pbuf_ref(es->p)),并且把next保存在全局变量中es->p=ptr->next;

然后再去查新的Item是否符合发送条件:

while((wr_err==ERR_OK)&&es->p&&(es->p->len<=tcp_sndbuf(tpcb)))

注:当时我没理解pbuf,看到发送和接受释放pbuf的机制不同,把我搞蒙圈了。

如果不符合发送条件,就退出。那么剩下的部分是怎么发送的:

//lwIP tcp_sent的回调函数(当从远端主机接收到ACK信号后发送数据)
err_t tcp_client_sent(void *arg, struct tcp_pcb *tpcb, u16_t len)
{
	struct tcp_client_struct *es;
	LWIP_UNUSED_ARG(len);
	es=(struct tcp_client_struct*)arg;
	if(es->p)tcp_client_senddata(tpcb,es);//发送数据
	return ERR_OK;
}

在这回调函数里面,这个不是发送函数,这个是发送完成收到ACK信号后的回调函数,开始我一直以为是发送函数,蒙圈。

说明:

1、tcp_client_connection_close时候,注意释放未发送的pbuf,否则内存泄漏;

 

你可能感兴趣的:(网络)