嵌牛5

姓名 李泽浩 学号 21181214372 学院 广州研究院

转载自https://blog.csdn.net/FPGADesigner/article/details/88765028

【嵌牛导读】本文是pbuf操作函数介绍与示例程序的使用详解

【嵌牛鼻子】pbuf操作函数介绍与示例程序

【嵌牛提问】pbuf操作函数介绍与示例程序

【嵌牛正文】

从前面几个UDP的程序实例中我们可以体会到pbuf的重要性,对pbuf的灵活操作也是完成程序功能和提高代码效率的关键。本篇总结lwip的pbuf.c中的常用函数并给出示例程序,其中部分函数和string.h文件中提供的传统内存操作函数功能相同。

基于上一篇中的UDP echo服务器设计,主要修改接收回调函数,将收到的数据做一定处理后,用串口打印或网口原路返回的方式查看效果。

1. pbuf_strstr

该函数在pbuf p中查找substr。但和strstr()函数不同,不会在遇到第一个’\0’时停止。

u16_t pbuf_strstr(struct pbuf* p, const char* substr)

1

第一个参数p是待搜索的pbuf,最大长度为0xFFFE。第二个参数substr是要在p中搜索的字符串。找到该字符串时返回索引,未找到则返回0xFFFF。

/* test1 pbuf_strstr */

u16_t pos;

pos = pbuf_strstr(p, "hello");

if (pos == 0xFFFF)

xil_printf("Havn't found 'hello'.\r\n");

else

xil_printf("find 'hello' at %d", pos);

1

2

3

4

5

6

7

关键代码如上,查找接收数据中的’hello’字符串。测试结果如下:

2. pbuf_memfind

该函数在pbuf p中查找出现的长度为mem_len的mem,查找位置从start_offest开始。

u16_t pbuf_memfind(struct pbuf* p, const void* mem, u16_t mem_len, u16_t start_offset)

1

p的最大长度也是0xFFFE。返回值意义与pbuf_strstr函数相同。该函数可以用start_offest指定在p中开始搜索的偏移位置。

/* test2 pbuf_strstr */

u16_t pos;

pos = pbuf_memfind(p, "hello", 5, 1);

if (pos == 0xFFFF)

xil_printf("Havn't found 'hello'.\r\n");

else

xil_printf("find 'hello' at %d\r\n", pos);

1

2

3

4

5

6

7

关键代码如上,查找接收数据中的’hello’字符串,偏移设置为1。测试结果如下,注意第二条发送hello没有被检测到,第三条被检测到了,这便是偏移量的作用:

3. pbuf_memcmp

该函数将指定偏移量的pbuf内容与内存s2进行比较,二者长度均为n。

u16_t pbuf_memcmp(struct pbuf* p, u16_t offset, const void* s2, u16_t n)

1

offset是开始比较的p的偏移位置;n是比较缓冲区的长度。如果二者相等则返回0。

u16_t pos;

pos = pbuf_memcmp(p, 1, "hello", 5);

if (pos != 0)

xil_printf("NOT EQUAL.\r\n");

else

xil_printf("EQUAL.\r\n");

1

2

3

4

5

6

关键代码如上,比较p中偏移为1开始的5个字节是否等于“hello”。测试结果如下,三条信息都有hello,但只有第一条被检测为相等:

4. pbuf_get_at

该函数从pbuf中的指定位置获取一个字节。

u8_t pbuf_get_at(struct pbuf* p, u16_t offset)

1

p是待解析的pbuf;offset是待返回字节在p中的偏移量。该函数返回偏移处的字节,但如果设置的offset大于pbuf的tot_len,则返回0。

/* test4 pbuf_get_at */

u8_t get_byte;

get_byte = pbuf_get_at(p, 1);

xil_printf("GET %c\r\n", get_byte);

1

2

3

4

关键代码如上,比较p中偏移为1的字节。测试结果如下:

5. pbuf_coalesce

该函数将一个pbuf队列合并为一个单独的pbuf。

struct pbuf* pbuf_coalesce(struct pbuf *p, pbuf_layer layer)

1

参数p是源pbuf;layer是新建pbuf的层。分配成功时返回一个单独的pbuf,p-next指向NULL,同时源pbuf被释放;分配失败时仍然返回旧的pbuf。因此需要我们检查分配结果。

6. pbuf_take

该函数将应用程序提供的数据复制到pbuf中。

err_t pbuf_take(struct pbuf *buf, const void *dataptr, u16_t len)

1

参数buf是要填充数据的pbuf;dataptr是应用程序中的数据缓冲区;len是数据缓冲区的长度。操作成功则返回ERR_OK;如果pbuf小于要复制的长度,则返回ERR_MEM。

struct pbuf* q;

q = pbuf_alloc(PBUF_TRANSPORT, 12, PBUF_RAM);

memset(q->payload, 0, 12);

err_t err;

err = pbuf_take(q, "hello world!", 12);

if (err != ERR_OK)

xil_printf("error on pbuf_take: %d.\r\n", err);

udp_printf(q, tpcb, addr, port);

pbuf_free(q);

1

2

3

4

5

6

7

8

9

关键代码如上,其与memcpy函数的作用相同。发送字符触发接收回调,测试结果如下:

7. pbuf_copy_partial

该函数与pbuf_take函数相反,是将pbuf(包缓冲区)中的内容复制到应用程序的缓冲区中。

u16_t pbuf_copy_partial(struct pbuf *buf, void *dataptr, u16_t len, u16_t offset)

1

参数buf是数据源;datatpr是应用程序中的缓冲区;len是复制的数据长度;offset是从buf中开始复制的偏移位置,从这里开始复制len个字节。该函数返回复制了的字节数,如果操作失败则返回0。

/* test7 pbuf_copy_partial */

char uart_send_buf[6] = "";

u16_t copynum;

copynum = pbuf_copy_partial(p, (void *)uart_send_buf, 5, 1);

xil_printf(uart_send_buf);

xil_printf("\r\ncopy number: %d.\r\n", copynum);

1

2

3

4

5

6

关键代码如上,从p偏置1处开始复制5个字节到uart_send_buf中。测试结果如下,注意当p中的字符不够5个时,pbuf_copy_partial只复制了三个字节:

8. pbuf_copy

该函数创建pbuf的一个PBUF_RAM类型的副本。该函数只会复制一个包,不会复制整个包队列。

err_t pbuf_copy(struct pbuf *p_to, struct pbuf *p_from)

1

将p_from复制到p_to。成功则返回ERR_OK;如果有一个pbuf为NULL,或者p_to不够大无法装下p_from,则返回ERR_ARG。

/* test8 pbuf_copy */

struct pbuf* p_to = NULL;

p_to = pbuf_alloc(PBUF_TRANSPORT, 20, PBUF_RAM);

memset(p_to->payload, 0, 20);

err_t err = pbuf_copy(p_to, p);

if (err != ERR_OK)

xil_printf("error on pbuf_copy: %d.\r\n", err);

udp_printf(p_to, tpcb, addr, port);

pbuf_free(p_to);

1

2

3

4

5

6

7

8

9

关键代码如上,p_to分配了20个字节,将接收到的信息复制到p_to中再发回。第一条copy成功;第二条由于p_to大小不够复制失败,报错-14对应的正是ERR_ART。

9. pbuf_alloc

该函数用于分配指定类型的pbuf,分配的实际内存由设置的pbuf层和请求的大小决定。其函数原型如下:

struct pbuf * pbuf_alloc(pbuf_layer layer, u16_t length, pbuf_type type)

1

第一个参数pbuf层定义头大小;第二个参数length决定pbuf有效载荷的大小;第三个参数pbuf类型决定pbuf的方式和位置。pbuf类型共有四种:

PBUF_RAM:以大块(chunk)的形式为pubf分配缓冲区内存和协议头。

PBUF_ROM:不会为pbuf分配缓冲区内存和协议头,在分配另一个pbuf并链到ROM型pbuf前时,必须为其预先分配协议头。这种pbuf使用的内存和ROM很相似,不可变且不会被更改。动态内存应该使用PBUF_REF。

PBUF_REF:通用不会为pbuf分配缓冲区内存和协议头。假设pbuf只在单个线程中使用,当pbuf进入队列时要调用pbuf_take来复制缓冲区。

PBUF_POOL:pbuf被分配作为一个pbuf链,大量数据时使用。

pbuf_alloc会返回分配的pbuf。如果分配了多个pbuf,则返回pbuf链中的第一个pbuf。

10. pbuf_alloced_custom

该函数用于初始化已分配的pbuf,相当于自定义pbuf。

struct pbuf* pbuf_alloced_custom(pbuf_layer l, u16_t length, pbuf_type type, struct pbuf_custom *p, void *payload_mem, u16_t payload_mem_len)

1

参数l为层标志,定义头的大小;参数length是pbuf有效载荷的长度;参数type为pbuf的类型;参数p指向要初始化的已分配了的pbuf;参数pay_load_mem指向用于有效载荷和头的缓冲区,该缓冲区必须能容纳下lengh+头的大小,也可以设置为NULL,在后面再进行设置;参数mem_len即为“payload_mem”缓冲区的大小。

11. pbuf_realloc

该函数用于将pbuf链缩小到所需的长度。虽然它叫“realloc”,但它并不能增加pbuf链的大小。

void pbuf_realloc(struct pbuf *p, u16_t new_len)

1

p为待缩小的pbuf;new_len是新的pbuf链长。根据设置的长度,pbuf链的前几个pbuf保持不变,释放一部分pbuf,并调整新链中的最后一个pbuf的大小。

如果操作的是ROM/REF类型的pbuf,其并不是pbuf链,因此只会调整其tot_len和len的值。该函数也不能在包队列上调用。

12. pbuf_free

pbuf的引用计数器相当于指向pbuf的指针数量。pbuf_free函数将减少对pbuf链或队列的引用次数。引用次数减到0时会释放pbuf。对于一个数据链,该函数对链中的每个pbuf都重复这个过程,直到第一个pbuf在递减后引用次数为非0位置。当链中所有的引用次数都是1时,则整个链被释放。

u8_t pbuf_free(struct pbuf *p)

1

该函数会返回链中从头开始释放的pbuf数量。比如一个链“a->b->c”,调用pbuf_free(a):

当前引用次数为“3->3->3”,结果为“2->3->3”;

当前引用次数为“1->2->3”,结果为“free->3->3”;

当前引用次数为“1->1->2”,结果为“free->free->1”;

当前引用次数为“2->1->1”,结果为“1->1->1”;

当前引用次数为“1->1->1”,结果为“free->free->free”;

13. pbuf_clen

该函数用于计算一个链中pbuf的数量。

u8_t pbuf_clen(struct pbuf *p)

1

参数p是链中的第一个pbuf。返回链中pbuf的数量。该函数与pbuf_cat一起测试。

14. pbuf_ref

该函数增加pbuf的引用次数。

void pbuf_ref(struct pbuf *p)

1

15. pbuf_cat

该函数用于连接两个pbuf,每个pbuf都可能是一个pbuf链。

void pbuf_cat(struct pbuf *h, struct pbuf *t)

1

使用此函数后,我们仅需要操作头部的pbuf即可,表示我们后面再也不会引用尾部的pbuf。

/* test15 pbuf_cat and pbuf_clen*/

struct pbuf* q;

q = pbuf_alloc(PBUF_TRANSPORT, 13, PBUF_RAM);

memset(q->payload, 0, 13);

err_t err = pbuf_take(q, "FPGADesigner:", 13);

if (err != ERR_OK)

xil_printf("error on pbuf_take: %d.\r\n", err);

pbuf_cat(q, p);

udp_printf(q, tpcb, addr, port);

u8_t pnum = pbuf_clen(q);

xil_printf("The number of pbuf in q is:%d.\r\n", pnum);

pbuf_free(q);

1

2

3

4

5

6

7

8

9

10

11

12

关键代码如上,将本地的q和接收到的p连接在一起,后面无需再对p进行任何操作。测试结果如下,可看到pbuf_clen得到的连接后的pbuf链中由2个pbuf。

16. pbuf_chain

该函数也是将两个pbuf或pbuf链连接在一起。pbuf必须属于同一个包,且不要在包队列上调用此函数。使用pbuf_chain表示后续还可能会用pbuf_dechain解链,因此当不再使用t时必须用pbuf_free(t)释放它。如果希望连接后不再使用t,应该使用pbuf_cat函数。

void pbuf_chain(struct pbuf *h, struct pbuf *t)

1

参数h是连接后pbuf(链)的头;参数t是连接后pbuf(链)的尾。使用该函数会调整链中所有pbuf的tot_len字段、头的最后一个pbuf的next字段、尾的第一个pbuf的ref字段。

该函数的测试代码与pbuf_cat基本相同,只是最后还需要释放作为连接尾部的p,而pbuf_cat不需要。下面pbuf_dechain的例子也用了pbuf_chain,这里不再赘述。

17. pbuf_dechain

该函数将第一个pbuf与链中剩余的pbuf分离开。该函数不能在包队列上调用。

struct pbuf * pbuf_dechain(struct pbuf *p)

1

参数p是待解链的pbuf。该函数返回pbuf链的剩余部分。

/* test16 pbuf_chain and pbuf_dechain*/

struct pbuf* q;

q = pbuf_alloc(PBUF_TRANSPORT, 13, PBUF_RAM);

memset(q->payload, 0, 13);

err_t err = pbuf_take(q, "FPGADesigner:", 13);

if (err != ERR_OK)

xil_printf("error on pbuf_take: %d.\r\n", err);

pbuf_chain(p, q);      //连接

udp_printf(p, tpcb, addr, port);

q = pbuf_dechain(p);    //解链

udp_printf(q, tpcb, addr, port);

pbuf_free(q);

pbuf_free(p);    //释放pbuf

1

2

3

4

5

6

7

8

9

10

11

12

13

核心代码如上,将收到的p连接本地q,沿原路发送时只操作p即可;再对pbuf链p解链,去掉第一个p,恢复出q,再沿原路发送时还可以操作q。下面是测试结果。

————————————————

版权声明:本文为CSDN博主「FPGADesigner」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:https://blog.csdn.net/FPGADesigner/article/details/88765028

你可能感兴趣的:(嵌牛5)