lwIP TCP/IP 协议栈笔记之十六: NETCONN 接口编程

本文详细介绍了LwIP中的netbuf结构体及其相关函数,包括netbuf_new(), netbuf_delete(), netconn_new()等,以及netconn结构体和相关函数接口,如netconn_bind(), netconn_connect()等。内容涵盖netbuf的数据结构、申请与释放、拷贝等操作,以及netconn的创建、连接、监听、接收和发送数据等,还提供了TCP Client、TCP Server和UDP的示例代码。" 108530908,9360597,商米DevOps转型实践:从蒸汽到高铁时代,"['运维', '大数据', 'DevOps', '代码管理', '自动化']
摘要由CSDN通过智能技术生成

目录

1. netbuf 结构体

2. netbuf 相关函数说明

2.1 netbuf_new()

2.2 netbuf_delete()

2.3 netbuf_alloc()

2.4 netbuf_free()

2.5 netbuf_ref()

2.6 netbuf_chain()

2.7 netbuf_data()

2.8 netbuf_next()与netbuf_first()

2.9 netbuf_copy()

2.10 netbuf_take()

3. netconn 结构体

4. netconn 函数接口说明

4.1 netconn_new()

4.2 netconn_delete()

4.3 netconn_getaddr()

4.4 netconn_bind()

4.5 netconn_connect()

4.6 netconn_disconnect()

4.7 netconn_listen()

4.8 netconn_accept()

4.9 netconn_recv()

4.10 netconn_send()

4.11 netconn_sendto()

4.12 netconn_write()

5. 示例例程

5.1 TCP Client

5.2 TCP Server

5.3 UDP


1. netbuf 结构体

LwIP 为了更好描述应用线程发送与接收的数据,并且为了更好管理这些数据的缓冲区,LwIP 定义了一个netbuf 结构体,它是基于pbuf 上更高一层的封装,记录了主机的IP 地址与端口号,在这里再提醒一下大家,端口号对应的其实就是应用线程。在接收的时候,应用程序肯定需要知道到底是谁发数据给自己,而在发送的时候,应用程序需要将自己的端口号与IP 地址填充到netbuf 结构体对应字段中。

// netbuf.h
/** "Network buffer" - contains data and addressing info */
struct netbuf {
  struct pbuf *p, *ptr;
  ip_addr_t addr;
  u16_t port;
#if LWIP_NETBUF_RECVINFO || LWIP_CHECKSUM_ON_COPY
  u8_t flags;
  u16_t toport_chksum;
#if LWIP_NETBUF_RECVINFO
  ip_addr_t toaddr;
#endif /* LWIP_NETBUF_RECVINFO */
#endif /* LWIP_NETBUF_RECVINFO || LWIP_CHECKSUM_ON_COPY */
};

1):netbuf 的p 字段的指针指向pbuf 链表,这是基于pbuf 上封装的结构体,因此,ptr 字段的指针也是指向pbuf,但是它与p 字段的指针有一点不一样,因为它可以指向任意的pbuf,由netbuf_next()与netbuf_first()函数来控制。

2):   addr 字段记录了数据发送方的IP 地址

3):port 记录了数据发送方的端口号

netbuf 结构体指向示意图具体见图,虚线表示ptr 指针的指向位置是不固定的,它是由netbuf_next()函数与netbuf_first()函数来调整的

指向不同类型的pbuf 链表 

指向相同类型pbuf 链表

 

2. netbuf 相关函数说明

netbuf 是LwIP 描述用户数据很重要的一个结构体,因为LwIP 是不可能让我们直接操作pbuf 的,因为分层的思想,应用数据必然是由用户操作的,因此LwIP 会提供很多函数接口让用户对netbuf 进行操作,无论是UDP 报文还是TCP 报文段,其本质都是数据,要发送出去的数据都会封装在netbuf 中,然后通过邮箱发送给内核线程(tcpip_thread 线程),然后经过内核的一系列处理,放入发送队列中,然后调用底层网卡发送函数进行发送,反之,应用线程接收到数据,也是通过netbuf 进行管理,下面一起来看看LwIP 提供给我们操作netbuf 的相关函数。

2.1 netbuf_new()

函数的功能是申请一个新的netbuf 结构体内存空间,通过memp 内存池进行申请,大小为MEMP_NETBUF,并且将netbuf 结构体全部初始化为0,并且返回一个指向netbuf结构体的指针,此时的netbuf 结构体的p 与ptr 字段不指向任何的pbuf.

struct
netbuf *netbuf_new(void)
{
  struct netbuf *buf;

  buf = (struct netbuf *)memp_malloc(MEMP_NETBUF);
  if (buf != NULL) {
    memset(buf, 0, sizeof(struct netbuf));
  }
  return buf;
}

2.2 netbuf_delete()

与netbuf_new()函数相反,释放一个netbuf 结构体内存空间,如果netbuf 结构体的p或者ptr 字段指向的pbuf 是拥有数据的,那么对应的pbuf 也会被释放掉

void
netbuf_delete(struct netbuf *buf)
{
  if (buf != NULL) {
    if (buf->p != NULL) {
      pbuf_free(buf->p);
      buf->p = buf->ptr = NULL;
    }
    memp_free(MEMP_NETBUF, buf);
  }
}

2.3 netbuf_alloc()

为netbuf 结构体中的p 字段指向的数据区域分配指定大小的内存空间,简单来说就是申请pbuf 内存空间,由于这个函数是在应用层调用的,因此这个内存会包含链路层首部、IP 层首部与传输层首部大小,当然,这些空间是附加上去的,用户指定的是数据区域大小,当然还有很重要的一点就是,如果当前netbuf 中已经存在数据区域了,那么这个数据区域会被释放掉,然后重新申请用户指定大小的数据区域,而函数的返回是一个指向数据区域起始地址的指针(即pbuf 的payload 指针)

void *
netbuf_alloc(struct netbuf *buf, u16_t size)
{
  LWIP_ERROR("netbuf_alloc: invalid buf", (buf != NULL), return NULL;);

  /* Deallocate any previously allocated memory. */
  if (buf->p != NULL) {
    pbuf_free(buf->p);
  }
  buf->p = pbuf_alloc(PBUF_TRANSPORT, size, PBUF_RAM);
  if (buf->p == NULL) {
    return NULL;
  }
  LWIP_ASSERT("check that first pbuf can hold size",
              (buf->p->len >= size));
  buf->ptr = buf->p;
  return buf->p->payload;
}

2.4 netbuf_free()

直接释放netbuf 结构体指向的pbuf 内存空间,如果结构体中指向pbuf 的内容为空,则不做任何释放操作,直接将p 与ptr 字段的指针设置为NULL

void
netbuf_free(struct netbuf *buf)
{
  LWIP_ERROR("netbuf_free: invalid buf", (buf != NULL), return;);
  if (buf->p != NULL) {
    pbuf_free(buf->p);
  }
  buf->p = buf->ptr = NULL;
#if LWIP_CHECKSUM_ON_COPY
  buf->flags = 0;
  buf->toport_chksum = 0;
#endif /* LWIP_CHECKSUM_ON_COPY */
}

2.5 netbuf_ref()

函数与netbuf_alloc()函数很像,都是申请内存空间,但是,有一个很大的不同,netbuf_ref()函数只申请pbuf 首部的内存空间,包含链路层首部、IP 层首部与传输层首部,而不会申请数据区域内存空间,然后把pbuf 的payload 指针指向用户指定的数据区域起始地址dataptr,这种申请经常在发送静态数据的时候用到,因为数据保存的地址是固定的,而不用动态申请,如果netbuf 的p 或者ptr 字段已经指向了pbuf,那么这些pbuf 将被释放掉.

注意:在使用该函数的时候用户需要传递有效的静态数据区域起始地址,比如某个静态字符串的起始地址。

err_t
netbuf_ref(struct netbuf *buf, const void *dataptr, u16_t size)
{
  LWIP_ERROR("netbuf_ref: invalid buf", (buf != NULL), return ERR_ARG;);
  if (buf->p != NULL) {
    pbuf_free(buf->p);
  }
  buf->p = pbuf_alloc(PBUF_TRANSPORT, 0, PBUF_REF);
  if (buf->p == NULL) {
    buf->ptr = NULL;
    return ERR_MEM;
  }
  ((struct pbuf_rom *)buf->p)->payload = dataptr;
  buf->p->len = buf->p->tot_len = size;
  buf->ptr = buf->p;
  return ERR_OK;
}

2.6 netbuf_chain()

netbuf_chain()函数是将tail 中的pbuf 数据连接到head 中的pbuf 后面,形成一个pbuf链表,在调用此函数之后,会将tail 结构删除

void
netbuf_chain(struct netbuf *head, struct netbuf *tail)
{
  LWIP_ERROR("netbuf_chain: invalid head", (head != NULL), return;);
  LWIP_ERROR("netbuf_chain: invalid tail", (tail != NULL), return;);
  pbuf_cat(head->p, tail->p);
  head->ptr = head->p;
  memp_free(MEMP_NETBUF, tail);
}

2.7 netbuf_data()

获取netbuf中的数据指针和数据长度。

err_t
netbuf_data(struct netbuf *buf, void **dataptr, u16_t *len)
{
  LWIP_ERROR("netbuf_data: invalid buf", (buf != NULL), return ERR_ARG;);
  LWIP_ERROR("netbuf_data: invalid dataptr", (dataptr != NULL), return ERR_ARG;);
  LWIP_ERROR("netbuf_data: invalid len", (len != NULL), return ERR_ARG;);

  if (buf->ptr == NULL) {
    return ERR_BUF;
  }
  *dataptr = buf->ptr->payload;
  *len = buf->ptr->len;
  return ERR_OK;
}

2.8 netbuf_next()与netbuf_first()

netbuf_next()用于移动netbuf 的ptr 数据指针,使ptr 指针指向pbuf 链表的下一个pbuf。同样的netbuf_first()函数可以将ptr 指针指向pbuf 链表的第一个pbuf。这两个函数是很有用的,比如netbuf 中p 字段的指针指向一个pbuf 链表,并且pbuf 链表中拥有多个pbuf,那么需要配合netbuf_data()函数将链表中的所有的pbuf 读取并且处理;如果netbuf_next()函数的返回值为0,表示调整成功,而如果返回值小于0 时,则表示调整失败

s8_t
netbuf_next(struct netbuf *buf)
{
  LWIP_ERROR("netbuf_next: invalid buf", (buf != NULL), return -1;);
  if (buf->ptr->next == NULL) {
    return -1;
  }
  buf->ptr = buf->ptr->next;
  if (buf->ptr->next == NULL) {
    return 1;
  }
  return 0;
}

void
netbuf_first(struct netbuf *buf)
{
  LWIP_ERROR("netbuf_first: invalid buf", (buf != NULL), return;);
  buf->ptr = buf->p;
}

2.9 netbuf_copy()

这个函数用于将netbuf 结构体数据区域pbuf 中的所有数据拷贝到dataptr 指针指向的存储区,即使pbuf(链表)中的数据被保存在多个pbuf 中,它也会完全拷贝出来,len 参数指定要拷贝数据的最大长度,如果netbuf 的数据区域空间小于len 指定的大小,那么内核只会拷贝netbuf 数据区域大小的数据,此外,该函数本质是一个宏定义,真正实现的函数在pbuf.c

#define netbuf_copy_partial(buf, dataptr, len, offset) \
  pbuf_copy_partial((buf)->p, (dataptr), (len), (offset))
#define netbuf_copy(buf,dataptr,len) netbuf_copy_partial(buf, dataptr, len, 0)

2.10 netbuf_take()

函数用于将用户指定区域的数据dataptr 拷贝到netbuf 结构体数据区域pbuf 中,可能用户数据太多,一个pbuf 存储不下用户的数据,那么内核将对数据进行切割处理,使用多个pbuf 存储,len 参数指定要拷贝数据的长度

#define netbuf_take(buf, dataptr, len) pbuf_take((buf)->p, dataptr, len)

/**
 * @ingroup pbuf
 * Copy application supplied data into a pbuf.
 * This function can only be used to copy the equivalent of buf->tot_len data.
 *
 * @param buf pbuf to fill with data
 * @param dataptr application supplied data buffer
 * @param len length of the application supplied data buffer
 *
 * @return ERR_OK if successful, ERR_MEM if the pbuf is not big enough
 */
err_t
pbuf_take(struct pbuf *buf, const void *dataptr, u16_t len);
#define netbuf_len(buf)              ((buf)->p->tot_len)
#define netbuf_fromaddr(buf)         (&((buf)->addr))
#define netbuf_set_fromaddr(buf, fromaddr) ip_addr_set(&((buf)->addr), fromaddr)
#define netbuf_fromport(buf)         ((buf)->port)

3. netconn 结构体

在LwIP 中,如TCP 连接,UDP 通信,都是需要提供一个编程接口给用户使用的,那么为了描述这样子的一个接口,LwIP 抽象出来一个nettonn 结构体,它能描述一个连接,供应用程序使用,同时内核的NETCONN API 接口也对各种连接操作函数进行了统一的封装,这样子,用户程序可以很方便使netconn 和编程函数,我们暂且将netconn 称之为连接结构体。

一个连接结构体中包含的成员变量很多,如描述连接的类型,连接的状态(主要是在TCP 连接中使用),对应的控制块(如UDP 控制块、TCP 控制块等等),还有对应线程的消息邮箱以及一些记录的信息.

api.h

/** A netconn descriptor */
struct netconn {
  /** 类型netconn (TCP, UDP or RAW) */
  enum netconn_type type;
  /** 当前状态 the netco
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值