Linux下网络缓冲区——chainbuffer的具体设计

目录

一、网络缓冲区

二、结构体的创建

1、所需要的结构体:结点和表

2、一些宏定义

3、返回整体的长度

4、buffer表的创建

三、buffer结点的添加

5、buf_chain_new:结点的创建

6、buf_chain_free_all:释放这个节点以后的全部结点

7、buffer_free:将全部进行释放

8、free_empty_chains:释放空的结点

9、buf_chain_insert:将创建好的结点进行插入

10、buf_chain_insert_new:先进行创建后进行插入

11、buf_chain_should_realign:进行判断是否需要扩容

12、buf_chain_align:在chain中进行移动操作

13、buffer_add:在buffer表中进行添加chain结点操作

四、将buffer表重置

14、buf_copyout:复制数据

15、zero_chain:将buffer表进行置空操作

16、buffer_drain:清洗buffer表

17、buffer_remove:将buffer进行重置

四、寻找buffer

18、check_sep:找到之后取出数据,并返回

19、buffer_search:通过分隔符进行查找操作

五、将存储的所有buffer全部取出来。

20、buffer_write_atmost:将数据全部取出


一、网络缓冲区

关于网络缓冲区的理论讲解,在我之前的文章中讲解过,这里不过多赘述。下面通过讲解具体的代码进行理解。

二、结构体的创建

1、所需要的结构体:结点和表

//结点
struct buf_chain_s {
    struct buf_chain_s *next;   
    uint32_t buffer_len;        //buffer的长度
    uint32_t misalign;          //这个buffer使用了的
    uint32_t off;               //实际可发数据的长度
    uint8_t *buffer;            //存放数据的具体位置
};

//表
struct buffer_s {
    buf_chain_t *first;     //大表的头
    buf_chain_t *last;      //大表的尾指针
    buf_chain_t **last_with_datap;      //二级指针,用于指向一个结点的next
    uint32_t total_len;                 //整个长度
    uint32_t last_read_pos; // for sep read 上次读取的位置
};

2、一些宏定义

#define CHAIN_SPACE_LEN(ch) ((ch)->buffer_len - ((ch)->misalign + (ch)->off))
#define MIN_BUFFER_SIZE 1024
#define MAX_TO_COPY_IN_EXPAND 4096
#define BUFFER_CHAIN_MAX_AUTO_SIZE 4096
#define MAX_TO_REALIGN_IN_EXPAND 2048
#define BUFFER_CHAIN_MAX 16*1024*1024  // 16M
#define BUFFER_CHAIN_EXTRA(t, c) (t *)((buf_chain_t *)(c) + 1)
#define BUFFER_CHAIN_SIZE sizeof(buf_chain_t)

3、返回整体的长度

uint32_t
buffer_len(buffer_t *buf) {
    return buf->total_len;      //返回整体的长度
}

4、buffer表的创建

buffer_t *
buffer_new(uint32_t sz) {
    (void)sz;           //转化成void类型
    buffer_t * buf = (buffer_t *) malloc(sizeof(buffer_t));     //将开辟的空间变成buffer_t的结构体的类型
    if (!buf) {
        return NULL;
    }
    memset(buf, 0, sizeof(*buf));
    buf->last_with_datap = &buf->first;         //现在刚创建好,所以将要插入的位置指向第一块地址也就是first。
    return buf;
}

三、buffer结点的添加

下面的函数不能按着顺序看,因为他的函数调用很多,比较乱,所以应该按着13、10、5、9、8、6、11、12,因为他们的调用很乱,但是写代码的时候,是按着顺序写的,只是其中要补充的函数很多。

5、buf_chain_new:结点的创建


//开辟chain结点,通过优化开辟的空间
static buf_chain_t *
buf_chain_new(uint32_t size) {
    buf_chain_t *chain;     
    uint32_t to_alloc;          //可以分配的大小
    if (size > BUFFER_CHAIN_MAX - BUFFER_CHAIN_SIZE)        //如果这个结点的大小大于可以存放数据的大小
        return (NULL);
    size += BUFFER_CHAIN_SIZE;      //存放数据的大小要加上这个结点结构体的大小,这样才是整个结点的大小
    
    if (size < BUFFER_CHAIN_MAX / 2) {      //如果整体的大小小于整体的一半可以通过下面进行优化一下
        to_alloc = MIN_BUFFER_SIZE;         //通过下面的循环操作,通过<<左移,这是一种动态内存分配的策略
        while (to_alloc < size) {           //也就是分配的内存略大于所需的内存,用于最小的浪费。
            to_alloc <<= 1;
        }
    } else {                                //这里不小于一半
        to_alloc = size;
    }
    if ((chain = malloc(to_alloc)) == NULL)     
        return (NULL);
    memset(chain, 0, BUFFER_CHAIN_SIZE);
    chain->buffer_len = to_alloc - BUFFER_CHAIN_SIZE;           //用于存放数据的大小
    chain->buffer = BUFFER_CHAIN_EXTRA(uint8_t, chain);         //这个存放数据的地址通过向后偏移一位,让它后面就是存放数据的地方
    return (chain);                                             //返回这个chain结点,这里的chain结点,只开辟了结点的空间,指定了后面数据的长度
}

6、buf_chain_free_all:释放这个节点以后的全部结点


//这个节点是空的,那么后面的结点也都是空的,全部释放掉,当然这个也可以用来销毁
static void 
buf_chain_free_all(buf_chain_t *chain) {
    buf_chain_t *next;
    for (; chain; chain = next) {
        next = chain->next;
        free(chain);
    }
}

7、buffer_free:将全部进行释放

//通过上面的释放操作,我们可以释放掉全部的结点
void
buffer_free(buffer_t *buf) {
    buf_chain_free_all(buf->first);
}

8、free_empty_chains:释放空的结点

//free掉空的结点
static buf_chain_t **
free_empty_chains(buffer_t *buf) {
    buf_chain_t **ch = buf->last_with_datap;        //指向buf的last_with_datap
    while ((*ch) && (*ch)->off != 0)                //找出空的结点
        ch = &(*ch)->next;
    if (*ch) {                                      //有结点,但为空
        buf_chain_free_all(*ch);             //释放
        *ch = NULL;
    }
    return ch;                                      //返回这个空的地址
}

9、buf_chain_insert:将创建好的结点进行插入


//找到存放这个结点的位置,把这个结点放入进去
static void
buf_chain_insert(buffer_t *buf, buf_chain_t *chain) {
    if (*buf->last_with_datap == NULL) {            //这里是指向空地址的
        buf->first = buf->last = chain;             
    } else {                                        //当不为空的时候
        buf_chain_t **chp;
        chp = free_empty_chains(buf);               //将其中为空的结点给释放掉,如果没有空的,就找到最后进行插入操作。
        *chp = chain;
        if (chain->off)                             
            buf->last_with_datap = chp;             //不为空就进行插入操作。
        buf->last = chain;                          //挪动尾指针
    }
    buf->total_len += chain->off;                   
}

10、buf_chain_insert_new:先进行创建后进行插入


//插入新节点的具体操作,先创建chain结点,然后将这个结点放入到合适的位置
static inline buf_chain_t *
buf_chain_insert_new(buffer_t *buf, uint32_t datlen) {      
    buf_chain_t *chain;
    if ((chain = buf_chain_new(datlen)) == NULL)        //开辟好这个chain结点,但并未开辟存放数据的空间
        return NULL;
    buf_chain_insert(buf, chain);                      //通过将结点放入到合适的位置
    return chain;       //返回结点
}

11、buf_chain_should_realign:进行判断是否需要扩容


//调整buffer,查看是否需要扩容
static int
buf_chain_should_realign(buf_chain_t *chain, uint32_t datlen)       //是否要扩容 
{
    //通过判断
    return chain->buffer_len - chain->off >= datlen &&          
        (chain->off < chain->buffer_len / 2) &&
        (chain->off <= MAX_TO_REALIGN_IN_EXPAND);           
}

12、buf_chain_align:在chain中进行移动操作

//进行移动到可以用的地址去
static void
buf_chain_align(buf_chain_t *chain) {   //也就是腾挪到这个的前面去
    memmove(chain->buffer, chain->buffer + chain->misalign, chain->off);
    chain->misalign = 0;        //那么这个使用的就置为0。
}

13、buffer_add:在buffer表中进行添加chain结点操作


int buffer_add(buffer_t *buf, const void *data_in, uint32_t datlen) {
    buf_chain_t *chain, *tmp;       //创建结点和临时值
    const uint8_t *data = data_in;  //将传进来的数据,转化为char类型的
    uint32_t remain, to_alloc;      //剩余和开辟的空间
    int result = -1;
    if (datlen > BUFFER_CHAIN_MAX - buf->total_len) {           //如果要添加的数据大小大于剩余的空间的话,那就报错
        goto done;
    }

    if (*buf->last_with_datap == NULL) {        //如果这个buffer_t的指向下一块内存为空,那么直接让它接上这块内存。
        chain = buf->last;
    } else {                                    //不为空,比如刚创建的时候,指向了开头的位置(first),那么这个结点就直接放到第一个位置及就好了
        chain = *buf->last_with_datap;
    }

    //上面的操作是找到存放结点的位置,下面也有个查找存放结点的位置,但是不同
    //上面的是先放到一个地址,再这个地址上创建结点,然后还有个查找结点,这个查找结点,是从上面的位置从上往下找空的结点
    //如果有空的结点就进行删除操作,不然白占内存,没有则使用这块空地址。
    if (chain == NULL) {                            
        chain = buf_chain_insert_new(buf, datlen);      //这里的操作是找到位置,创建节点,查找结点,删除空结点,插入位置。
        if (!chain)                                     //也就是说到这里才仅仅创建好结点并找到个合适的位置。
            goto done;
    }

    remain = chain->buffer_len - chain->misalign - chain->off;              //剩余的大小
    if (remain >= datlen) {                                                 //在这里咱们通过判断可以使用的空间大小
        memcpy(chain->buffer + chain->misalign + chain->off, data, datlen);  //当可以使用了,当时不是只开辟了chain结点嘛,并没有开辟数据存放的位置的空间
        chain->off += datlen;                           //那么上面的操作就是到这个buffer存储空间+已经使用了的+可以使用的位置的地址
        buf->total_len += datlen;                       //也就是说直接开辟一块可以用来存放数据的空间。
        // buf->n_add_for_cb += datlen;
        goto out;                   
    } else if (buf_chain_should_realign(chain, datlen)) {       //当剩余的大小不足以存放这个数据的时候,咱们可以判断
        //比如这一块存放数据的地址,前面是已经使用了的数据,并且被取出来了,但是取出来之后,这个misalign会往后移动,那么造成前面的空间是未使用的
        //因此咱们需要判断上面真正可以使用的空间是否可以存放这块数据,当满足的时候,咱们只需要将这个misalign往前移动过去,并且重新置为空就好了。
        buf_chain_align(chain);     //移动位置,移动到可以存放这个数据的地址去。

        memcpy(chain->buffer + chain->off, data, datlen);   //然后赋值数据
        chain->off += datlen;
        buf->total_len += datlen;
        // buf->n_add_for_cb += datlen;
        goto out;
    }

    //但是当发现腾挪数据也不能满足的时候,就应该重新开辟空间了。
    to_alloc = chain->buffer_len;
    if (to_alloc <= BUFFER_CHAIN_MAX_AUTO_SIZE/2)       //这里还是内存动态规划
        to_alloc <<= 1;         //稍微大于所需的内存
    if (datlen > to_alloc)
        to_alloc = datlen;
    tmp = buf_chain_new(to_alloc);  //开辟新的空间
    if (tmp == NULL)
        goto done;
    if (remain) {
        memcpy(chain->buffer + chain->misalign + chain->off, data, remain);
        chain->off += remain;
        buf->total_len += remain;
        // buf->n_add_for_cb += remain;
    }

    data += remain;
    datlen -= remain;

    memcpy(tmp->buffer, data, datlen);
    tmp->off = datlen;
    buf_chain_insert(buf, tmp);
    // buf->n_add_for_cb += datlen;
out:
    result = 0;
done:
    return result;
}

四、将buffer表重置

这里的重置函数的观看顺序是17、14、16、15,也就是调用顺序。

14、buf_copyout:复制数据


//移动之前需要先进行复制
static uint32_t
buf_copyout(buffer_t *buf, void *data_out, uint32_t datlen) {
    buf_chain_t *chain;
    char *data = data_out;
    uint32_t nread;
    chain = buf->first;
    if (datlen > buf->total_len)
        datlen = buf->total_len;        //可以节省空间
    if (datlen == 0)
        return 0;
    nread = datlen;             //这里就是读取到多大的数据。

    //这个结点不可以存放这块数据的时候,
    while (datlen && datlen >= chain->off) {
        uint32_t copylen = chain->off;
        memcpy(data,
            chain->buffer + chain->misalign,
            copylen);
        data += copylen;
        datlen -= copylen;

        chain = chain->next;
    }
    if (datlen) {
        memcpy(data, chain->buffer + chain->misalign, datlen);
    }

    return nread;
}

15、zero_chain:将buffer表进行置空操作


//将这个buffer_t全部置为空
static inline void
ZERO_CHAIN(buffer_t *dst) {
    dst->first = NULL;
    dst->last = NULL;
    dst->last_with_datap = &(dst)->first;
    dst->total_len = 0;
}

16、buffer_drain:清洗buffer表


// 清洗数据,将全部数据置为空
int buffer_drain(buffer_t *buf, uint32_t len) {
    buf_chain_t *chain, *next;
    uint32_t remaining, old_len;
    old_len = buf->total_len;       //保存一下旧的数据大小
    if (old_len == 0)
        return 0;

    if (len >= old_len) {
        len = old_len;
        for (chain = buf->first; chain != NULL; chain = next) {
            next = chain->next;
            free(chain);        //将全部的数据进行释放
        }
        ZERO_CHAIN(buf);
    } else {
        buf->total_len -= len;
        remaining = len;
        for (chain = buf->first; remaining >= chain->off; chain = next) {
            next = chain->next;
            remaining -= chain->off;

            if (chain == *buf->last_with_datap) {
                buf->last_with_datap = &buf->first;
            }
            if (&chain->next == buf->last_with_datap)
                buf->last_with_datap = &buf->first;

            free(chain);
        }

        buf->first = chain;
        chain->misalign += remaining;
        chain->off -= remaining;
    }
    
    // buf->n_del_for_cb += len;
    return len;
}

17、buffer_remove:将buffer进行重置

//进行重置
int buffer_remove(buffer_t *buf, void *data_out, uint32_t datlen) {
    uint32_t n = buf_copyout(buf, data_out, datlen);        //返回读取的数据的大小
    if (n > 0) {
        if (buffer_drain(buf, n) < 0)
            n = -1;
    }
    return (int)n;
}

四、寻找buffer

在buffer中存在一种叫做分隔符的东西,可以通过这个分隔符就可以找到需要的buffer了。19、18。

18、check_sep:找到之后取出数据,并返回


static bool
check_sep(buf_chain_t * chain, int from, const char *sep, int seplen) {
    for (;;) {
        int sz = chain->off - from;
        if (sz >= seplen) {
            return memcmp(chain->buffer + chain->misalign + from, sep, seplen) == 0;
        }
        if (sz > 0) {
            if (memcmp(chain->buffer + chain->misalign + from, sep, sz)) {
                return false;
            }
        }
        chain = chain->next;
        sep += sz;
        seplen -= sz;
        from = 0;
    }
}

19、buffer_search:通过分隔符进行查找操作


//通过某些字符进行查找的操作。
int buffer_search(buffer_t *buf, const char* sep, const int seplen) {
    buf_chain_t *chain;
    int i;
    chain = buf->first;     //指向首地址
    if (chain == NULL)      
        return 0;
    int bytes = chain->off;                 
    while (bytes <= buf->last_read_pos) {   
        chain = chain->next;
        if (chain == NULL)
            return 0;
        bytes += chain->off;        //得到全部的数据长度
    }
    bytes -= buf->last_read_pos;        //减去上次查找的字符位置
    int from = chain->off - bytes;      
    for (i = buf->last_read_pos; i <= buf->total_len - seplen; i++) {   //开始查找分隔符
        if (check_sep(chain, from, sep, seplen)) {
            buf->last_read_pos = 0;
            return i+seplen;        //返回这一次的分隔符的位置
        }
        ++from; 
        --bytes;
        if (bytes == 0) {
            chain = chain->next;
            from = 0;
            if (chain == NULL)
                break;
            bytes = chain->off;
        }
    }
    buf->last_read_pos = i;     //更新最后一次的查找位置
    return 0;
}

五、将存储的所有buffer全部取出来。

20、buffer_write_atmost:将数据全部取出


//进行提取或复制出来
uint8_t * buffer_write_atmost(buffer_t *p) {
    buf_chain_t *chain, *next, *tmp, *last_with_data;
    uint8_t *buffer;        //char
    uint32_t remaining;     
    int removed_last_with_data = 0;     //用来进行标记的
    int removed_last_with_datap = 0;    

    chain = p->first;                   //指向首地址
    uint32_t size = p->total_len;       

    if (chain->off >= size) {       //查看第一块结点剩余的大小,如果有,那么使用这块内存
        return chain->buffer + chain->misalign;         //返回这块的地址
    }

    //如果第一块不够用,那么久进行重新开辟的操作
    //下面的操作是更新它的偏移量
    remaining = size - chain->off;
    for (tmp=chain->next; tmp; tmp=tmp->next) {     
        if (tmp->off >= (size_t)remaining)
            break;
        remaining -= tmp->off;      //每次删除每一块的大小。
    }

    //如果不
    if (chain->buffer_len - chain->misalign >= (size_t)size) {
        /* already have enough space in the first chain */
        size_t old_off = chain->off;
        buffer = chain->buffer + chain->misalign + chain->off;  //将这个buffer指向了这块可以发送数据的地址
        tmp = chain;
        tmp->off = size;
        size -= old_off;
        chain = chain->next;
    } else {
        if ((tmp = buf_chain_new(size)) == NULL) {
            return NULL;
        }
        //开辟个新的结点存储这些
        buffer = tmp->buffer;
        tmp->off = size;
        p->first = tmp;
    }

    //进行遍历链表的操作,并进行复制数据,
    last_with_data = *p->last_with_datap;
    for (; chain != NULL && (size_t)size >= chain->off; chain = next) {
        next = chain->next;

        if (chain->buffer) {
            memcpy(buffer, chain->buffer + chain->misalign, chain->off);    //每次遍历,将数据赋值到buffer中去
            size -= chain->off;
            buffer += chain->off;
        }
        if (chain == last_with_data)    //指向了最后一个结点
            removed_last_with_data = 1;
        if (&chain->next == p->last_with_datap)     //如果这个结点是last_with_datap指向的,那么标记
            removed_last_with_datap = 1;

        free(chain);    //将得到数据的结点进行释放操作。
    }

    //处理剩余的数据
    if (chain != NULL) {
        memcpy(buffer, chain->buffer + chain->misalign, size);
        chain->misalign += size;
        chain->off -= size;
    } else {
        p->last = tmp;
    }

    tmp->next = chain;

    //更新链表的状态
    if (removed_last_with_data) {
        p->last_with_datap = &p->first;
    } else if (removed_last_with_datap) {
        if (p->first->next && p->first->next->off)
            p->last_with_datap = &p->first->next;
        else
            p->last_with_datap = &p->first;
    }
    return tmp->buffer + tmp->misalign;          //返回处理后的数据地址
}

本篇文章讲解完毕!https://xxetb.xetslk.com/s/2D96kH

  • 16
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值