先看一下数据包结构体pbuf

/* pbuf结构体 */
struct pbuf {struct pbuf *next; //用于将pbuf连接成链表void *payload;        //数据指针u16_t tot_len;        //当前以及后续所有pbuf包含的数据总长度u16_t len;            //当前pbuf的数据长度u8_t type;         //pbuf类型u8_t flags;         //状态位u16_t ref;         //pbuf被引用的次数
};

pbuf中有个成员type,表示pbuf的类型。pbuf共有四种类型PBUF_REF、PBUF_ROM、PBUF_POOL和PBUF_RAM。

/* pbuf类型 */
typedef enum {PBUF_RAM,         /* pbuf和数据来自连续的内存堆 */PBUF_ROM,      /* pbuf来自内存池MEMP_PBUF,数据来自ROM */PBUF_REF,       /* pbuf来自内存池MEMP_PBUF,数据来自RAM */PBUF_POOL       /* pbuf和数据来自一个或多个内存池MEMP_PBUF_POOL */
} pbuf_type;

再看一下pbuf_layer,表示层类型,上层数据包需要为下层预留足够空间用来填充头部

/* pbuf分层类型 */
typedef enum {PBUF_TRANSPORT,   //传输层PBUF_IP,                   //网络层PBUF_LINK,             //链路层PBUF_RAW               //原始层
} pbuf_layer;

下面看一下申请pbuf

/* 申请pbuf */
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;/* 根据pbuf所在层计算头部偏移量 */offset = 0;switch (layer) {case PBUF_TRANSPORT:offset += PBUF_TRANSPORT_HLEN;case PBUF_IP:offset += PBUF_IP_HLEN;case PBUF_LINK:offset += PBUF_LINK_HLEN;break;case PBUF_RAW:break;default:return NULL;}/* 判断pbuf类型 */switch (type) {/* PBUF_POOL类型 */case PBUF_POOL:/* 从内存池MEMP_PBUF_POOL申请第一个单元 */p = memp_malloc(MEMP_PBUF_POOL);if (p == NULL) {return NULL;}/* 填充pbuf成员 */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));p->ref = 1;/* 判断一个MEMP_PBUF_POOL单元够不够,如果不够则继续申请 */r = p;rem_len = length - p->len;while (rem_len > 0) {/* 从内存池MEMP_PBUF_POOL申请单元 */q = memp_malloc(MEMP_PBUF_POOL);if (q == NULL) {pbuf_free(p);return NULL;}/* 填充pbuf成员 */q->type = type;q->flags = 0;q->next = NULL;/* 将pbuf连接起来 */r->next = q;/* 继续填充pbuf成员 */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;}break;/* PBUF_RAM类型 */case PBUF_RAM:/* 从内存堆申请pbuf和数据 */p = (struct pbuf *)mem_malloc(LWIP_MEM_ALIGN_SIZE(SIZEOF_STRUCT_PBUF + offset) + LWIP_MEM_ALIGN_SIZE(length));if (p == NULL) {return NULL;}/* 填充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_ROM和PBUF_REF类型 */case PBUF_ROM:case PBUF_REF:/* 从内存堆申请pbuf */p = memp_malloc(MEMP_PBUF);if (p == NULL) {return NULL;}/* 填充pbuf成员 */p->payload = NULL;p->len = p->tot_len = length;p->next = NULL;p->type = type;break;default:return NULL;}/* 引用次数为1 */p->ref = 1;/* 状态为清空 */p->flags = 0;return p;
}

PBUF_RAM型

PBUF_POOL型

PBUF_ROM/PBUF_REF型

下面看一下pbuf释放,当没有任何对象引用该pbuf的时候才能释放,否则引用次数减一。

/* 释放pbuf */
u8_t pbuf_free(struct pbuf *p)
{u16_t type;struct pbuf *q;u8_t count;if (p == NULL) {return 0;}/* 循环释放链表上的pbuf */count = 0;while (p != NULL) {u16_t ref;/* 引用次数减1 */ref = --(p->ref);/* 如果引用次数为0,则释放 */if (ref == 0) {/* 释放之前先找到下一个pbuf指针 */q = p->next;/* 根据pbuf类型,释放pbuf内存 */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);}/* 释放个数加1 */count++;/* 更新下一个pbuf指针到当前p */p = q;} /* 引用次数不为0,则停止释放 */else {p = NULL;}}return count;
}

其它,lwip还提供了其它很多的API

收缩数据区函数,从数据区尾部进行释放

/* 收缩pbuf数据区 */
void pbuf_realloc(struct pbuf *p, u16_t new_len)
{struct pbuf *q;u16_t rem_len;s32_t grow;/* 只能收缩不能扩展 */if (new_len >= p->tot_len) {return;}/* 数据扩展长度(负值) */grow = new_len - p->tot_len;/* 更新链表中每一个pbuf的tot_len变量,找出需要切割的第一个pbuf指针 */rem_len = new_len;q = p;while (rem_len > q->len) {rem_len -= q->len;q->tot_len += (u16_t)grow;q = q->next;}/* 当前pbuf类型为PBUF_RAM,并且该pbuf需要切割 */if ((q->type == PBUF_RAM) && (rem_len != q->len)) {/* 当前pbuf收缩内存 */q = mem_realloc(q, (u8_t *)q->payload - (u8_t *)q + rem_len);}/* 调整当前pbuf数据长度和后续数据长度 */q->len = rem_len;q->tot_len = q->len;/* 释放后续pbuf */if (q->next != NULL) {pbuf_free(q->next);}q->next = NULL;
}

调整头部pbuf有效数据指针

/* 调整pbuf有效数据指针 */
u8_t pbuf_header(struct pbuf *p, s16_t header_size_increment)
{u16_t type;void *payload;u16_t increment_magnitude;if ((header_size_increment == 0) || (p == NULL))return 0;/* 调整长度 */if (header_size_increment < 0){increment_magnitude = -header_size_increment;} else {increment_magnitude = header_size_increment;}type = p->type;/* pbuf类型为PBUF_RAM和PBUF_POOL */payload = p->payload;if (type == PBUF_RAM || type == PBUF_POOL) {/* 调整pbuf有效数据指针 */p->payload = (u8_t *)p->payload - header_size_increment;/* 预留空间不够调整 */if ((u8_t *)p->payload < (u8_t *)p + SIZEOF_STRUCT_PBUF) {p->payload = payload;return 1;}} /* pbuf类型为PBUF_REF和PBUF_ROM */else if (type == PBUF_REF || type == PBUF_ROM) {/* 向后调整 */if ((header_size_increment < 0) && (increment_magnitude <= p->len)) {p->payload = (u8_t *)p->payload - header_size_increment;}/* 不允许向前调整 */else {return 1;}}else {return 1;}/* 更新pbuf数据长度和pbuf链表数据总长度 */p->len += header_size_increment;p->tot_len += header_size_increment;return 0;
}

统计pbuf个数

/* 统计链表中有多少个pbuf */
u8_t pbuf_clen(struct pbuf *p)
{u8_t len;len = 0;while (p != NULL) {++len;p = p->next;}return len;
}

引用次数加一

/* pbuf链表中第一个pbuf引用数加1 */
void pbuf_ref(struct pbuf *p)
{if (p != NULL) {++(p->ref);}
}

将两个pbuf链表拼接起来,pbuf_cat和pbuf_chain。

pbuf_cat,t原来的调用者不需要使用pbuf_free。

pbuf_chain,t原来的调用者需要使用pbuf_free。

/* 将两个pbuf链表拼接起来 */
void pbuf_cat(struct pbuf *h, struct pbuf *t)
{struct pbuf *p;/* 调整第一个链表中pbuf的tot_len值 */for (p = h; p->next != NULL; p = p->next) {p->tot_len += t->tot_len;}p->tot_len += t->tot_len;/* 将两个pbuf链表拼接起来 */p->next = t;
}
/* 将两个pbuf链表拼接起来,并且后一个pbuf链表中第一个pbuf引用数加1 */
void pbuf_chain(struct pbuf *h, struct pbuf *t)
{pbuf_cat(h, t);pbuf_ref(t);
}

解开pbuf

/* 将第一个pbuf和后面的链表解开 */
struct pbuf *pbuf_dechain(struct pbuf *p)
{struct pbuf *q;u8_t tail_gone = 1;/* 第一个pbuf后面还有pbuf */q = p->next;if (q != NULL) {/* 调整后面pbuf数据的总长度 */q->tot_len = p->tot_len - p->len;/* 更新第一个pbuf参数 */p->next = NULL;p->tot_len = p->len;/* 解开后释放一次引用 */tail_gone = pbuf_free(q);if (tail_gone > 0) {}}/* 解开后pbuf链表首节点指针 */return ((tail_gone > 0) ? NULL : q);
}

数据拷贝

pbuf_copy,pbuf之间拷贝数据

pbuf_copy_partial,将pbuf数据拷贝出来

pbuf_take,将数据拷贝进pbuf

/* pbuf之间拷贝 */
err_t pbuf_copy(struct pbuf *p_to, struct pbuf *p_from)
{u16_t offset_to=0, offset_from=0, len;/* 遍历p_from链表 */do{/* 计算这次拷贝的数据长度 */if ((p_to->len - offset_to) >= (p_from->len - offset_from)) {len = p_from->len - offset_from;} else {len = p_to->len - offset_to;}/* 将数据拷贝过来 */MEMCPY((u8_t*)p_to->payload + offset_to, (u8_t*)p_from->payload + offset_from, len);/* 调整偏移量 */offset_to += len;offset_from += len;/* 目的pbuf数据区已满,切换下一个pbuf */if (offset_to == p_to->len) {offset_to = 0;p_to = p_to->next;}/* 源pbuf数据区已空,切换下一个pbuf */if (offset_from >= p_from->len) {offset_from = 0;p_from = p_from->next;}/* 检查错误 */if((p_from != NULL) && (p_from->len == p_from->tot_len)) {}if((p_to != NULL) && (p_to->len == p_to->tot_len)) {}} while (p_from);return ERR_OK;
}
/* 将pbuf中指定长度、指定位置的数据拷贝到指定内存 */
u16_t pbuf_copy_partial(struct pbuf *buf, void *dataptr, u16_t len, u16_t offset)
{struct pbuf *p;u16_t left;u16_t buf_copy_len;u16_t copied_total = 0;left = 0;if((buf == NULL) || (dataptr == NULL)) {return 0;}/* 遍历pbuf链表 */for(p = buf; len != 0 && p != NULL; p = p->next) {/* 数据不在当前pbuf中 */if ((offset != 0) && (offset >= p->len)) {offset -= p->len;} /* 数据在当前pbuf中 */else {/* 计算该pbuf中可以拷贝的数据长度 */buf_copy_len = p->len - offset;if (buf_copy_len > len)buf_copy_len = len;/* 将pbuf数据拷贝数据缓冲区 */MEMCPY(&((char*)dataptr)[left], &((char*)p->payload)[offset], buf_copy_len);/* 计算已经拷贝的数据长度 */copied_total += buf_copy_len;left += buf_copy_len;/* 计算剩余数据长度 */len -= buf_copy_len;/* 清空偏移量 */offset = 0;}}/* 拷贝的数据长度 */return copied_total;
}
/* 将数据拷贝到pbuf链表 */
err_t pbuf_take(struct pbuf *buf, const void *dataptr, u16_t len)
{struct pbuf *p;u16_t buf_copy_len;u16_t total_copy_len = len;u16_t copied_total = 0;if ((buf == NULL) || (dataptr == NULL) || (buf->tot_len < len)) {return ERR_ARG;}/* 遍历整个pbuf链表 */for(p = buf; total_copy_len != 0; p = p->next) {/* 计算该pbuf中可以拷贝的数据长度 */buf_copy_len = total_copy_len;if (buf_copy_len > p->len) {buf_copy_len = p->len;}/* 将数据拷贝pbuf数据缓冲区 */MEMCPY(p->payload, &((char*)dataptr)[copied_total], buf_copy_len);/* 计算剩余数据长度和已经拷贝数据长度 */total_copy_len -= buf_copy_len;copied_total += buf_copy_len;}return ERR_OK;
}

克隆pbuf

/* 在内存堆中克隆一个指定层的pbuf链表,并释放原有pbuf链表 */
struct pbuf *pbuf_coalesce(struct pbuf *p, pbuf_layer layer)
{struct pbuf *q;err_t err;if (p->next == NULL) {return p;}/* 从内存堆申请pbuf空间 */q = pbuf_alloc(layer, p->tot_len, PBUF_RAM);if (q == NULL) {return p;}/* 将数据从p拷贝到q */err = pbuf_copy(q, p);/* 释放原pbuf链表 */pbuf_free(p);/* 返回新的pbuf链表指针 */return q;
}

LwIP之数据包管理相关推荐

  1. LWIP的数据包管理

    1.数据包结构-pbuf 1.1.pbuf结构 LWIP是TCP/IP协议栈的一种具体实现,本质就是对数据包的处理,在LWIP中使用一个被称为pbuf的结构管理数据包,LWIP源码中的pbuf.c和p ...

  2. LWIP学习笔记(3)LWIP数据包管理结构pbuf

    pbuf:LWIP是TCP/IP协议栈的一种具体体现,本质就是对数据包的处理,在LWIP中使用一个被称为pbuf的结构体管理数据包,LWIP源码中的pbuf.c和pbuf.h这两个文件就是关于pbuf ...

  3. STM32 LWIP TCP 数据包分包后合并,超过591字节后分会分包问题

    最新在使用LWIP + MQTT的应用过程中,出现一个很奇怪的问题,当上位机反送的JSON长度过大时,我的stm32收到的数据就会有问题: 上图中成功接收到失败接收口相关1个字节,用wireshark ...

  4. linux管理外部工具,linux – 除了iptables之外的数据包管理工具?

    我正在寻找可以根据一组规则改变网络数据包的有效内容的 linux实用程序.理想情况下,我会使用iptables和netfilter内核模块,但它们不支持通用的有效负载调整:iptables会改变各种头 ...

  5. LWIP (chapter 2.01) pbuf数据包缓存

    网络中的数据所如果存在多个副本,会在一定程度上让费内存空间和CPU资源.通过引用的方式可以很大程度解决这个问题. 网络数据包是通过pbuf的数据结构管理的.pbuf和数据包所占用的空间可以动态申请或者 ...

  6. lwip之数据收发流程_1

    lwip从逻辑上看也是分为4层:链路层.网络层(IP.ARP.(ICMP.IGMP这两个协议是网络层的补充协议,并不严格属于网络层)).传输层(TCP.UDP).应用层,基本等同TCP/IP,只是各层 ...

  7. Python 精选笔试面试习题—类继承、方法对象、包管理、闭包、可变类型作为默认参数、列表引用、sort与sorted、 append 和 extend、深拷贝和浅拷贝

    1. 类继承 如下代码 class A(object):def show(self):print 'This is calss A'class B(A):def show(self):print 'T ...

  8. linux禁止向指定ip发送数据包,Linux下使用iptables封锁端口禁止邮件发送

    原创文章,转载请注明出处:https://www.myzhenai.com/thread-17843-1-1.html https://www.myzhenai.com.cn/post/2161.ht ...

  9. 如果某路由器到达目的网络有三种方式:通过RIP;通过静态路由;通过默认路由,那么路由器会根据哪种方式进行转发数据包?( )

    如果某路由器到达目的网络有三种方式:通过RIP;通过静态路由;通过默认路由,那么路由器会根据哪种方式进行转发数据包?( ) A. 通过RIP B. 通过静态路由 C. 通过默认路由 D. 都可以 答案 ...

最新文章

  1. c语言初学 循环 的灵活使用小案例
  2. Ubuntu环境下挂载新硬盘 --硬盘要挂载在某个文件夹下面
  3. python 示例_带有示例的Python列表copy()方法
  4. Linux虚拟机添加磁盘
  5. GitHub超详细图文攻略 - Git客户端下载安装 GitHub提交修改源码工作流程 Git分支 标签 过滤 Git版本工作流
  6. DB2数据库常用命令
  7. 位移密码加密、解密matlab实现
  8. C语言三位数的整数立方和,c语言求一个三位数正整数n各个数位上数据的立方和...
  9. 微信小程序点击事件传递自定义参数的方法
  10. LCN分布式事务(Java)
  11. C++的游戏--贪吃蛇
  12. iOS- 延迟1秒执行一个函数
  13. 风控策略的自动化生成-利用决策树分分钟生成上千条策略
  14. 【英语】--动起来的英语进度
  15. 台式计算机32位和64位的区别,电脑操作系统中32位和64位到底有哪些区别?
  16. Linux配置NTP同步,出现Server dropped: no data解决
  17. 使用 Tkprof 分析 ORACLE 跟踪文件
  18. linux系统重启网卡命令
  19. 提升python运行效率_“提”是多音字吗?“提”字的多音字能组成哪些词语?
  20. myplay.pif、winsys16.dll、scrsys16.dll和AlxRes.exe的分析与解决

热门文章

  1. Qt工作笔记-双击TreeWidget列进行数据修改
  2. Qt实现全屏下玫瑰花(含详细注释)
  3. 海洋zyapi.php,海洋cms自带资源发布api插件和第三方资源站接入办法说明
  4. java excel 兼容问题_java--POI解析excel兼容性问题
  5. java发送消息_通过java给qq邮箱发送信息
  6. python 线性回归 统计检验 p值_PAST:最简便易用的统计学分析软件教程(一)软件基本信息介绍...
  7. (王道408考研操作系统)第三章内存管理-第二节4:页面分配策略
  8. C++11 并发指南四(future 详解三 std::future std::shared_future)
  9. 182. 查找重复的电子邮箱
  10. golang操作mysql数据库(Go-SQL-Driver/MySQL)