IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> LWIP学习笔记-数据包管理 -> 正文阅读

[网络协议]LWIP学习笔记-数据包管理

背景

TCP/IP 是一种数据通信机制,本质是对数据包进行处理。
链路层判断收到数据包类型,会提取数据字段,记录主机物理地址信息;
IP层提取IP地址,实现数据的存储、转发,根据数据包编号实现数据包重装,提取数据包中关于传输层的信息,向传输层递交数据包并记录递交结果;
传输层中TCP使用数据包信息更新TCP状态机,并向应用程序递交数据。

数据包管理应该是一种高效管理机制,使协议栈各层对数据灵活处理同时还能减少数据在各层间传递时的时间和空间开销。
协议栈本是严格分层的,但是这会使层间数据递交非常慢,为避免这种情况,代码内部没有采用完整的分层结构,部分数据结构和实现原理再其他层是可见的。协议栈实现时,仍然是贯彻分层思想,每层都是独立模块实现,提供 输入输出函数。但是不是严格的分层机制,各层级存在交叉存取现象。
一般来讲,协议栈是设计为内核代码的一部分,与用户层保持着完全的分层封装。但是小型嵌入式设备中,内核空间和用户空间没有明显的分层现象,这就允许用户和内核之间有更多宽松的数据处理机制。

LWIP采用如下进程模型:
在这里插入图片描述
协议栈作为一个操作系统的独立进程,用户可以留驻在协议栈进程 中,也可以是一个独立的进程。第一种方式,用户与协议栈通信通过回调函数;第二种方式,用户和协议栈通信需要调用系统提供的信号量和邮箱机制。

PBUF结构体

设计PBUF包的核心是能容纳不同类型的数据,又能避免各层间数据拷贝。
结构体定义如下:

struct pbuf {
  /** next pbuf in singly linked pbuf chain */
  struct pbuf *next;
  /** pointer to the actual data in the buffer */
  void *payload;
  /**
   * total length of this buffer and all next buffers in chain
   * belonging to the same packet.
   *
   * For non-queue packet chains this is the invariant:
   * p->tot_len == p->len + (p->next? p->next->tot_len: 0)
   */
  u16_t tot_len;
  /** length of this buffer */
  u16_t len;
  /** pbuf_type as u8_t instead of enum to save space */
  u8_t /*pbuf_type*/ type;
  /** misc flags */
  u8_t flags;
  /**
   * the reference count always equals the number of pointers
   * that refer to this pbuf. This can be pointers from an application,
   * the stack itself, or pbuf->next pointers from a chain.
   */
  u16_t ref;
};
名称描述
next指向下一个pbuf数据结构
payload数据起始地址
tot_len当前和其后面所有pbuf有效数据总长度
len有效数据长度
typepbuf数据类型
flags没用到
ref该pbuf被引用的次数
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;

PBUF_RAM

PBUF_RAM类型是通过内存堆分配的,这种类型是协议栈使用最多的,协议栈中待发数据和应用程序的待发数据一般都采用这个形式。
下面是源代码申请这种类型:

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

分配空间大小有pbuf结构大小、数据存储空间大小、offset。分配成功后的数据结构如下图所示:
在这里插入图片描述
从图中可以看出,payload并非指向整个数据区的起始处,而是间隔了offset,这段长度内存通常是各种首部字段,如TCP报文首部、IP首部、以太网首部等。

PBUF_ROM

PBUF_ROM类型是内存池中分配一个相应的pbuf结构,不申请数据区的空间。发送一些静态数据,可以采用这种数据类型。
在这里插入图片描述

PBUF_REF

PBUF_REF同PBUF_ROM相同,内存池中分配一个相应的pbuf结构,不申请数据区的空间。

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

在这里插入图片描述

PBUF_POOL

PBUF_POOL类型是通过内存池分配得到,下面是源代码申请这种类型:

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

在这里插入图片描述

数据包管理

数据包申请函数pbuf_alloc(),有两个重要参数,一个是数据包pbuf类型,上面已经讲的很详细了;第二个是协议栈的层次,根据层次决定offset值不同。

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 */
  offset = 0;
  switch (layer) {
  case PBUF_TRANSPORT:
    /* add room for transport (often TCP) layer header */
    offset += PBUF_TRANSPORT_HLEN;
    /* FALLTHROUGH */
  case PBUF_IP:
    /* add room for IP layer header */
    offset += PBUF_IP_HLEN;
    /* FALLTHROUGH */
  case PBUF_LINK:
    /* add room for link layer header */
    offset += PBUF_LINK_HLEN;
    break;
  case PBUF_RAW:
    break;
  default:
    return NULL;
  }
  switch (type) {
  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 */
    p->payload = LWIP_MEM_ALIGN((void *)((u8_t *)p + (SIZEOF_STRUCT_PBUF + offset)));
    /* the total length of the pbuf chain is the requested size */
    p->tot_len = length;
    /* set the length of the first pbuf in the chain */
    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;
    /* now allocate the tail of the pbuf chain */
    /* 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? */
    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 */
      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);
      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;
  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? */
  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:
    return NULL;
  }
  /* set reference count */
  p->ref = 1;
  /* set flags */
  p->flags = 0;
  return p;
}

数据包释放函数pbuf_free(),需要注意的是能被删除的节点一定是pbuf链表的首节点或其他地方引用过的节点。

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

  if (p == NULL) {
    /* if assertions are disabled, proceed with debug output */
    return 0;
  }
  PERF_START;
  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;
    /* this pbuf is no longer referenced to? */
    if (ref == 0) {
      /* remember next pbuf in chain for next iteration */
      q = p->next;
      type = p->type;
#if LWIP_SUPPORT_CUSTOM_PBUF
      /* is this a custom pbuf? */
      if ((p->flags & PBUF_FLAG_IS_CUSTOM) != 0) {
        struct pbuf_custom *pc = (struct pbuf_custom*)p;
        pc->custom_free_function(p);
      } else
#endif /* LWIP_SUPPORT_CUSTOM_PBUF */
      {
        /* 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;
}

当删除某个pbuf结构时,会检测其类型,根据类型调用不同的释放函数释放。

  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2021-09-04 17:54:21  更:2021-09-04 17:55:35 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/20 9:31:44-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码