1:背景介绍:
在日常业务开发中,使用缓冲区进行临时存储的业务场景也比较多,如tcp每个连接底层都维持一个发送缓冲区和接收缓冲区。
实现一个ringbuffer,做代码备用。(可以考虑如何对ringbuffer进行扩大?)
//实现ringbuffer,其实就是申请一块内存,对塞入数据和取出数据位置分别进行管理
typedef struct RINGBUFF_T{
void * data;
unsigned int size;
unsigned int read_pos; //数据起始位置
unsigned int write_pos; //数据终止位置
}ringbuffer_t;
2:测试代码:
这里的代码是我为了测试,实现一个ringbuffer进行管理。
这里只考虑了单线程的逻辑实现,多线程需要适配一下。。。
这里在取数据时,有一定的设计,考虑取一个包的数据(可以自己适配调整)。
2.1:my_ringbuffer.h
#ifndef __RINGBUFFER_H_
#define __RINGBUFFER_H_
typedef struct RINGBUFF_T{
void * data;
unsigned int size;
unsigned int read_pos; //数据起始位置
unsigned int write_pos; //数据终止位置
}ringbuffer_t;
//创建ringbuffer
ringbuffer_t * ringbuffer_create(unsigned int size);
//销毁ringbuffer
void ringbuffer_destroy(ringbuffer_t * ring_buffer);
//往ringbuffer中存数据 写入
int ringbuffer_put(ringbuffer_t * ring_buffer, const char* buffer, unsigned int len);
//判断是否是完整的数据 然后进行处理
int ringbuffer_get_len(ringbuffer_t *ring_buffer);
//依赖ringbuffer_get_len 返回值申请内存,取出ring_buffer中的数据
int ringbuffer_get(ringbuffer_t * ring_buffer, char * buffer, unsigned int len);
//基本接口 外部基本不用,但是函数内部有使用
//重置缓冲区
void ringbuffer_reset(ringbuffer_t * ring_buffer);
//ringbuffer已经使用的内存空间的大小
int ringbuffer_use_len(ringbuffer_t * ring_buffer);
//ringbuffer没有使用的内存的大小
int ringbuffer_space_len(ringbuffer_t * ring_buffer);
//基本的判空和判满接口
int ringbuffer_isempty(ringbuffer_t * ring_buffer);
int ringbuffer_isfull(ringbuffer_t * ring_buffer);
// int get_ringbuffer_size(ringbuffer_t * ring_buffer);
#endif //__RINGBUFFER_H_
2.2:my_ringbuffer.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "my_ringbuffer.h"
static inline __attribute__((const))
int is_power_of_2(unsigned long n)
{
return (n != 0 && ((n & (n - 1)) == 0));
}
static unsigned long roundup_power_of_two(unsigned long n)
{
if((n & (n-1)) == 0)
return n;
unsigned long maxulong = (unsigned long)((unsigned long)~0);
unsigned long andv = ~(maxulong&(maxulong>>1));
while((andv & n) == 0)
andv = andv>>1;
return andv<<1;
}
//创建ringbuffer
ringbuffer_t * ringbuffer_create(unsigned int size)
{
//对入参进行校验 并且是2的次方
if (!is_power_of_2(size)) {
size = roundup_power_of_two(size);
}
ringbuffer_t * ring_buffer;
ring_buffer = (ringbuffer_t*)malloc(sizeof(*ring_buffer));
if(ring_buffer == NULL)
{
printf("create ringbuffer error \n");
return NULL;
}
ring_buffer->data = (void*)malloc(size);
if(ring_buffer->data == NULL)
{
printf("create ringbuffer data error \n");
free(ring_buffer);
return NULL;
}
ring_buffer->size = size;
ring_buffer->read_pos = 0;
ring_buffer->write_pos = 0;
return ring_buffer;
}
//销毁ringbuffer
void ringbuffer_destroy(ringbuffer_t * ring_buffer)
{
if(ring_buffer)
{
if(ring_buffer->data)
{
free(ring_buffer->data);
ring_buffer->data = NULL;
}
free(ring_buffer);
ring_buffer = NULL;
}
}
//往ringbuffer中存数据 写入
int ringbuffer_put(ringbuffer_t * ring_buffer, const char* buffer, unsigned int len)
{
if(ring_buffer->write_pos >=ring_buffer->read_pos &&(len <(ring_buffer->size - ring_buffer->write_pos +ring_buffer->read_pos)))
{
//进行拷贝
if(ring_buffer->size - ring_buffer->write_pos >len)
{
memcpy(ring_buffer->data + ring_buffer->write_pos, buffer, len);
ring_buffer->write_pos += len;
}else
{
unsigned int right_space_len = ring_buffer->size - ring_buffer->write_pos;
memcpy(ring_buffer->data + ring_buffer->write_pos, buffer, right_space_len);
memcpy(ring_buffer->data, buffer+right_space_len, len - right_space_len);
ring_buffer->write_pos = len - right_space_len;
}
return 0;
}
if(ring_buffer->write_pos <ring_buffer->read_pos && (ring_buffer->read_pos - ring_buffer->write_pos) >len)
{
memcpy(ring_buffer->data + ring_buffer->write_pos, buffer, len);
ring_buffer->write_pos += len;
return 0;
}
return -1;
}
//判断是否是完整的数据 然后进行处理
int ringbuffer_get_len(ringbuffer_t *ring_buffer)
{
//对ringbuffer中的数据做判断解析 如果是完整的数据 则提取出去
if(ringbuffer_use_len(ring_buffer) < strlen("FFFF0D0A<header><tail>0D0AFEFE"))
{
printf("ringbuffer data is error [%d], [%ld]\n", ringbuffer_use_len(ring_buffer), strlen("FFFF0D0A<header><tail>0D0AFEFE"));
return -1;
}
//判断是否是终结的字段
const char* end_str = "<tail>0D0AFEFE";
char check_end_str[20] = {0};
if(ring_buffer->write_pos >strlen(end_str))
{
memcpy(check_end_str, ring_buffer->data+ring_buffer->write_pos - (strlen(end_str)), strlen(end_str));
}else
{
unsigned int left_len = ring_buffer->write_pos;
memcpy(check_end_str, ring_buffer->data +ring_buffer->size - (strlen(end_str) - left_len), ring_buffer->size - (strlen(end_str) - left_len));
memcpy(check_end_str + (strlen(end_str) - left_len), ring_buffer->data, left_len);
}
printf("get check_end_str is %s \n", check_end_str);
char * ret_addr = strstr(check_end_str, end_str);
if(ret_addr == NULL)
{
return -1;
}
if(check_end_str - ret_addr != 0)
{
printf("ddddD :why end string is error");
return -1;
}
return ringbuffer_use_len(ring_buffer);
}
//从ringbuffer中取数据做处理, 判断接收到的字符是否是终结符号,就可以去做处理
//取完数据后重置ringbuffer的位置 读取
int ringbuffer_get(ringbuffer_t * ring_buffer, char * buffer, unsigned int len)
{
//这里建立在ringbuffer_get_len 的基础上,传入入参,取出数据
int data_len = ringbuffer_use_len(ring_buffer);
if(data_len >= len)
{
printf("para buffer is not enough space \n");
return -1;
}
if(ring_buffer->write_pos >ring_buffer->read_pos )
{
printf("get data from ringbuffer len: [%d] \n", ring_buffer->write_pos - ring_buffer->read_pos);
memcpy(buffer, ring_buffer->data + ring_buffer->read_pos, data_len);
}else
{
memcpy(buffer, ring_buffer->data+ring_buffer->read_pos, ring_buffer->size - ring_buffer->read_pos);
memcpy(buffer+ring_buffer->size - ring_buffer->read_pos, ring_buffer->data, data_len - (ring_buffer->size - ring_buffer->read_pos));
}
ring_buffer->write_pos = 0;
ring_buffer->read_pos = 0;
return 0;
}
//直接从socket中读数据放入ringbuffer中也可以
int ringbuffer_get_from_dev()
{
return 0;
}
//直接从ringbuffer中取数据用socket进行发送
int ringbuffer_put_to_dev()
{
return 0;
}
void ringbuffer_reset(ringbuffer_t * ring_buffer)
{
ring_buffer->read_pos = ring_buffer->write_pos = 0;
}
int ringbuffer_use_len(ringbuffer_t * ring_buffer)
{
if(ring_buffer->write_pos >= ring_buffer->read_pos)
{
return ring_buffer->write_pos-ring_buffer->read_pos;
}
return ring_buffer->write_pos + ring_buffer->size - ring_buffer->read_pos;
}
int ringbuffer_space_len(ringbuffer_t * ring_buffer)
{
if(ring_buffer->write_pos >= ring_buffer->read_pos)
{
return ring_buffer->read_pos +(ring_buffer->size - ring_buffer->write_pos);
}
return ring_buffer->read_pos - ring_buffer->write_pos;
}
int ringbuffer_isempty(ringbuffer_t * ring_buffer)
{
return ringbuffer_use_len(ring_buffer) == 0? 0 :-1;
}
int ringbuffer_isfull(ringbuffer_t * ring_buffer)
{
return ringbuffer_space_len(ring_buffer) == 0? 0 :-1;
}
// int get_ringbuffer_size(ringbuffer_t * ring_buffer)
// {
// return ring_buffer->size;
// }
2.3:my_ringbuffer_test.c
/************************************************
info: 对ringbuffer的封装做简单的测试
data: 2022/02/10
author: hlp
************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "tcp_ringbuffer.h"
int main()
{
//申请一个ringbuffer
ringbuffer_t * ringbuff = ringbuffer_create(128);
printf("ringbuffer_create size is : %d \n", ringbuff->size);
printf("ringbuffer isempty [%d] \n", ringbuffer_isempty(ringbuff));
printf("ringbuffer isfull [%d] \n", ringbuffer_isfull(ringbuff));
printf("ringbuffer used len is [%d] \n",ringbuffer_use_len(ringbuff));
printf("ringbuffer space len is [%d] \n", ringbuffer_space_len(ringbuff));
ringbuffer_destroy(ringbuff);
//判断基本的条件
ringbuff = ringbuffer_create(129);
printf("ringbuffer_create size is : %d \n", ringbuff->size);
printf("ringbuffer isempty [%d] \n", ringbuffer_isempty(ringbuff));
printf("ringbuffer isfull [%d] \n", ringbuffer_isfull(ringbuff));
printf("ringbuffer used len is [%d] \n",ringbuffer_use_len(ringbuff));
printf("ringbuffer space len is [%d] \n", ringbuffer_space_len(ringbuff));
ringbuffer_destroy(ringbuff);
//给ringbuff中塞入一定的数据查看相关的基本信息
ringbuff = ringbuffer_create(129);
const char* str_data = "FFFF0D0A<header><tail>0D0AFEFE";
//给ringbuffer中塞入一定的数据,进行获取查看
ringbuffer_put(ringbuff, str_data, strlen(str_data));
printf("set data size is : %lu, ringbuff size is %d \n", strlen(str_data), ringbuff->size);
printf("ringbuffer isempty [%d] \n", ringbuffer_isempty(ringbuff));
printf("ringbuffer isfull [%d] \n", ringbuffer_isfull(ringbuff));
printf("ringbuffer used len is [%d] \n",ringbuffer_use_len(ringbuff));
printf("ringbuffer space len is [%d] \n", ringbuffer_space_len(ringbuff));
int data_len = ringbuffer_get_len(ringbuff);
if(data_len == -1)
{
printf("error of ringbuff data \n");
}else
{
printf("ringbuff data len is %d \n", data_len);
}
char * data_exec;
data_exec = (char*)malloc(data_len +1);
memset(data_exec, 0, data_len +1);
printf("sizeof data_exec is %lu \n", sizeof(data_exec));
ringbuffer_get(ringbuff, data_exec, data_len +1);
printf("ringbuff get data is :%s \n",data_exec);
printf("ringbuffer isempty [%d] \n", ringbuffer_isempty(ringbuff));
printf("ringbuffer isfull [%d] \n", ringbuffer_isfull(ringbuff));
printf("ringbuffer used len is [%d] \n",ringbuffer_use_len(ringbuff));
printf("ringbuffer space len is [%d] \n", ringbuffer_space_len(ringbuff));
ringbuffer_destroy(ringbuff);
if(data_exec !=NULL)
{
free(data_exec);
data_exec = NULL;
}
printf("******************test of more package check***********************\n");
const char* data_str_test = "FFFF0D0A<header><tail>0D0AFEFE";
const char* data_str_test1 = "FFFF0D0A<header>111<tail>0D0AFEFE";
const char* data_str_test2 = "FFFF0D0A<header>222<tail>0D0AFEFE";
const char* data_str_test3 = "FFFF0D0A<header><tail>0D0AFEFE";
const char* data_str_test4 = "FFFF0D0A<header><tail>0D0AFEFE";
const char* data_str_error = "FFFF0D0A<header>test error";
//试着塞多个数据 能取出来吗?
ringbuff = ringbuffer_create(129);
ringbuffer_put(ringbuff, data_str_test, strlen(data_str_test));
printf("put one data. ringbuffer used len is [%d] \n",ringbuffer_use_len(ringbuff));
printf("put one data. ringbuffer space len is [%d] \n", ringbuffer_space_len(ringbuff));
ringbuffer_put(ringbuff, data_str_test1, strlen(data_str_test1));
printf("put two data. ringbuffer used len is [%d] \n",ringbuffer_use_len(ringbuff));
printf("put two data. ringbuffer space len is [%d] \n", ringbuffer_space_len(ringbuff));
ringbuffer_put(ringbuff, data_str_test2, strlen(data_str_test2));
printf("put three data. ringbuffer used len is [%d] \n",ringbuffer_use_len(ringbuff));
printf("put three data. ringbuffer space len is [%d] \n", ringbuffer_space_len(ringbuff));
printf("ringbuffer is not empty [%d] \n", ringbuffer_isempty(ringbuff));
printf("ringbuffer is not full [%d] \n", ringbuffer_isfull(ringbuff));
int test_get_len = ringbuffer_get_len(ringbuff);
if(test_get_len == -1)
{
printf("error of ringbuff data \n");
}else
{
printf("get ringbuff has data len is %d \n", test_get_len);
}
data_exec = (char*)malloc(test_get_len +1);
memset(data_exec, 0, test_get_len +1);
ringbuffer_get(ringbuff, data_exec, test_get_len +1);
printf("ringbuff get data is :%s \n",data_exec);
printf("get all data used len is [%d] \n",ringbuffer_use_len(ringbuff));
printf("get all data space len is [%d] \n", ringbuffer_space_len(ringbuff));
printf("ringbuffer isempty [%d] \n", ringbuffer_isempty(ringbuff));
printf("ringbuffer is not full [%d] \n", ringbuffer_isfull(ringbuff));
ringbuffer_destroy(ringbuff);
if(data_exec !=NULL)
{
free(data_exec);
data_exec = NULL;
}
ringbuff = ringbuffer_create(129);
ringbuffer_put(ringbuff, data_str_test, strlen(data_str_test));
ringbuffer_put(ringbuff, data_str_error, strlen(data_str_error));
test_get_len = ringbuffer_get_len(ringbuff);
if(test_get_len == -1)
{
printf("error of ringbuff data \n");
}else
{
printf("get ringbuff has data len is %d \n", test_get_len);
}
ringbuffer_put(ringbuff, data_str_test, strlen(data_str_test));
test_get_len = ringbuffer_get_len(ringbuff);
if(test_get_len == -1)
{
printf("error of ringbuff data \n");
}else
{
printf("get ringbuff has data len is %d \n", test_get_len);
}
data_exec = (char*)malloc(test_get_len +1);
memset(data_exec, 0, test_get_len +1);
ringbuffer_get(ringbuff, data_exec, test_get_len +1);
printf("ringbuff get data is :%s \n",data_exec);
printf("get all data used len is [%d] \n",ringbuffer_use_len(ringbuff));
printf("get all data space len is [%d] \n", ringbuffer_space_len(ringbuff));
ringbuffer_destroy(ringbuff);
if(data_exec !=NULL)
{
free(data_exec);
data_exec = NULL;
}
//根本不会有write追到read的场景,除非这里的设计做成不是全部取出,或者多线程处理
printf("******************* check \n");
return 0;
}
3:运行结果
这里我使用的gcc进行编译,没有用makefile
这里是为了符合特定的业务格式,有“FFFF0D0A
”和“0D0AFEFE”进行标识的数据才认为是一个完整的数据。
写这个测试代码主要是为了针对tcp接收缓冲区业务处理考虑的。
hlp@ubuntu:~/220107/0:test_ringbuffer_tcp_Stickybag$ ./ringbuffer
ringbuffer_create size is : 128
ringbuffer isempty [0]
ringbuffer isfull [-1]
ringbuffer used len is [0]
ringbuffer space len is [128]
ringbuffer_create size is : 256
ringbuffer isempty [0]
ringbuffer isfull [-1]
ringbuffer used len is [0]
ringbuffer space len is [256]
set data size is : 30, ringbuff size is 256
ringbuffer isempty [-1]
ringbuffer isfull [-1]
ringbuffer used len is [30]
ringbuffer space len is [226]
get check_end_str is <tail>0D0AFEFE
ringbuff data len is 30
sizeof data_exec is 8
get data from ringbuffer len: [30]
ringbuff get data is :FFFF0D0A<header><tail>0D0AFEFE
ringbuffer isempty [0]
ringbuffer isfull [-1]
ringbuffer used len is [0]
ringbuffer space len is [256]
******************test of more package check***********************
put one data. ringbuffer used len is [30]
put one data. ringbuffer space len is [226]
put two data. ringbuffer used len is [63]
put two data. ringbuffer space len is [193]
put three data. ringbuffer used len is [96]
put three data. ringbuffer space len is [160]
ringbuffer is not empty [-1]
ringbuffer is not full [-1]
get check_end_str is <tail>0D0AFEFE
get ringbuff has data len is 96
get data from ringbuffer len: [96]
ringbuff get data is :FFFF0D0A<header><tail>0D0AFEFEFFFF0D0A<header>111<tail>0D0AFEFEFFFF0D0A<header>222<tail>0D0AFEFE
get all data used len is [0]
get all data space len is [256]
ringbuffer isempty [0]
ringbuffer is not full [-1]
get check_end_str is der>test error
error of ringbuff data
get check_end_str is <tail>0D0AFEFE
get ringbuff has data len is 86
get data from ringbuffer len: [86]
ringbuff get data is :FFFF0D0A<header><tail>0D0AFEFEFFFF0D0A<header>test errorFFFF0D0A<header><tail>0D0AFEFE
get all data used len is [0]
get all data space len is [256]
******************* check
原文链接:https://blog.csdn.net/yun6853992/article/details/122913568
“RingBuffer.h”
注意是head指向了读区域,tail指向了写区域!
注意是head指向了读区域,tail指向了写区域!
注意是head指向了读区域,tail指向了写区域!
typedef struct { size_t rb_capacity; //缓冲区容量 char *rb_head; //用于读出的指针 char *rb_tail; //用于写入的指针 char rb_buff[256]; //缓冲区实体 }RingBuffer;
下面分析他的几个函数:
“RingBuffer.c”
//用来比较最小值的宏 #define min(a, b) (a)<(b)?(a)
b) //新建RingBuffer,给成员赋值 //MAX_RINGBUFFER_LEN 这个宏,被定义为"P0数据最大长度"的2倍 //head/tail 两个指针,都指向缓冲区实体(数组rb_buff)的首地址 void rb_new(RingBuffer* rb) { rb->rb_capacity = MAX_RINGBUFFER_LEN; //capacity; rb->rb_head = rb->rb_buff; rb->rb_tail = rb->rb_buff; };
获得缓冲区总容量Capacity:
size_t rb_capacity(RingBuffer *rb) { return rb->rb_capacity; }
获得缓冲区可读区域,返回可读区域大小:
三种情况:
1、head与tail都指向同一个地方时,可读区域大小为0【这种情况只会在缓冲区还未使用时出现,
开始使用之后,不会出现head/tail重合的现象,即tail永远不会等于head,否则head指向的数据还未读走就被覆盖了!】
2、head < tail ,说明tail没有写到缓冲区末尾,从缓冲区开头重新开始。可读的区域自然为(tail - head)
3、head > tail ,说明tail已经从缓冲区末尾写完,并从开头处重新准备写了。
插入图片给大家看看:
rb_buff是数组名,因此可以作为缓冲实体首地址的指
size_t rb_can_read(RingBuffer *rb) { if (rb->rb_head == rb->rb_tail) return 0; if (rb->rb_head < rb->rb_tail) return rb->rb_tail - rb->rb_head; return rb_capacity(rb) - (rb->rb_head - rb->rb_tail); }
获得可写区域大小,就可以用总容量 减去 可读区域大小来计算了:
size_t rb_can_write(RingBuffer *rb) { return rb_capacity(rb) - rb_can_read(rb); }
读数据,从head指向的地址开始,读到data指向的地址处,读count个数据。返回读的个数
三种情况:
1、head < tail ,此时要从count 和"可读区域大小"中选一个较小的值,作为读操作的次数。避免了count 大于“可读区域”的错误。
2、head > tail 且 count 的个数 小于“从head到缓冲区末尾的数据个数”图中蓝色。直接复制内存,再修改head 指针即可。
3、head > tail 且 count 的个数 大于“从head到缓冲区末尾的数据个数”。
此时,先把从head到缓冲区末尾的值蓝色复制到data处,再把剩余的绿色复制过去。注意两个值:copy_sz 和*(data + copy_sz)如图
这种情况下,问题来了,要是绿色的区域超过了tail 怎么办?:)
所以,应该加了一个判断,这个在写操作中做了,但这里没做。即要读的个数count 要小于可读区域的大小。
不然会出现head > tail 但head 指向的数据以及head 后边的数据又不是有效数据,这个问题。
代码:
size_t rb_read(RingBuffer *rb, void *data, size_t count) { if (rb->rb_head < rb->rb_tail) { int copy_sz = min(count, rb_can_read(rb)); memcpy(data, rb->rb_head, copy_sz); rb->rb_head += copy_sz; return copy_sz; } else { if (count < rb_capacity(rb)-(rb->rb_head - rb->rb_buff)) { int copy_sz = count; memcpy(data, rb->rb_head, copy_sz); rb->rb_head += copy_sz; return copy_sz; } else { int copy_sz = rb_capacity(rb) - (rb->rb_head - rb->rb_buff); memcpy(data, rb->rb_head, copy_sz); rb->rb_head = rb->rb_buff; copy_sz += rb_read(rb, (char*)data+copy_sz, count-copy_sz); return copy_sz; } } }
写数据,把数据从data指向的地址,写到tail 指向的地址,写count个。返回写的个数。
这里进来直接判断,要写入的内容大小 要小于可写区域大小,防止造成数据覆盖。写入合法。
下面写入分了三种情况:
1、2 需要计算tail_avail_sz,这个值为tail 到缓冲区末尾的数据区域大小。
1、head < tail ,count < tail_avail_sz 。直接复制内容。假如tail 到了缓冲区末尾,让tail 回到缓冲区首地址。
2、head < tail ,count > tail_avail_sz 。先写入 tail_avail_sz 个数据,tail 回到缓冲区首地址,再写入剩余的部分。
3、head > tail ,这种情况最简单,由于已经做了写入合法判断,所以直接复制内容,修改tail 即可。
代码:
size_t rb_write(RingBuffer *rb, const void *data, size_t count) { if (count >= rb_can_write(rb)) return -1; if (rb->rb_head <= rb->rb_tail) { int tail_avail_sz = rb_capacity(rb) - (rb->rb_tail - rb->rb_buff); if (count <= tail_avail_sz) { memcpy(rb->rb_tail, data, count); rb->rb_tail += count; if (rb->rb_tail == rb->rb_buff+rb_capacity(rb)) rb->rb_tail = rb->rb_buff; return count; } else { memcpy(rb->rb_tail, data, tail_avail_sz); rb->rb_tail = rb->rb_buff; return tail_avail_sz + rb_write(rb, (char*)data+tail_avail_sz, count-tail_avail_sz); } } else { memcpy(rb->rb_tail, data, count); rb->rb_tail += count; return count; } }
对于源程序中的,指针不为NULL判断,其实是必须要加上的,不知道为什么,我下载的代码,这些部分都被注释掉了。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。