知识巩固源码落实之3:缓冲区ringbuffer

news/2025/2/8 20:02:47 标签: 缓冲区

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

我开始试着积累一些常用代码:自己代码库中备用

我的知识储备更多来自这里,推荐你了解:Linux,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK等技术内容,立即学习


http://www.niftyadmin.cn/n/1520000.html

相关文章

leetcode945. 使数组唯一的最小增量

给定整数数组 A&#xff0c;每次 move 操作将会选择任意 A[i]&#xff0c;并将其递增 1。 返回使 A 中的每个值都是唯一的最少操作次数。 示例 1: 输入&#xff1a;[1,2,2] 输出&#xff1a;1 解释&#xff1a;经过一次 move 操作&#xff0c;数组将变为 [1, 2, 3]。示例 2:…

知识巩固源码落实之4:http get同步请求数据demo

1&#xff1a;背景介绍 http是基于C/S模式的&#xff0c;一直对如何实现http请求很疑惑。 http的协议理论了解很多&#xff0c;却一直无从下手不知道怎么实现一个http的请求。 作为一个http的客户端&#xff0c;请求服务器的一个消息&#xff0c;实现demo&#xff0c;做代码…

java并发关键字_java并发编程:关键字

一、关键字volatile(易变的)&#xff1a;1、保证了多线程操作的可见性&#xff1b;2、但是无法保证对变量的任何操作都是原子性的(如自增操作)&#xff1b;3、禁止指令重排序&#xff0c;能在一定程度上保证有序性&#xff1b;禁止指令重排序规则&#xff1a;1、多个volatile操…

leetcode面试题 17.16. 按摩师

一个有名的按摩师会收到源源不断的预约请求&#xff0c;每个预约都可以选择接或不接。在每次预约服务之间要有休息时间&#xff0c;因此她不能接受相邻的预约。给定一个预约请求序列&#xff0c;替按摩师找到最优的预约集合&#xff08;总预约时间最长&#xff09;&#xff0c;…

知识巩固源码落实之5:http get异步请求数据demo(多线程+struct epoll_event的ptr)

1&#xff1a;背景介绍 简单的实现一个客户端对http服务器的请求后&#xff0c;思考如果同时多个请求&#xff0c;如何有效的对代码进行设计&#xff0c;关注每个请求的回复。 1.1&#xff1a;如果多个请求同时进行&#xff0c;异步实现 http的特定是无连接的&#xff0c;一…

leetcode892. 三维形体的表面积

在 N * N 的网格上&#xff0c;我们放置一些 1 * 1 * 1 的立方体。 每个值 v grid[i][j] 表示 v 个正方体叠放在对应单元格 (i, j) 上。 请你返回最终形体的表面积。 示例 1&#xff1a; 输入&#xff1a;[[2]] 输出&#xff1a;10示例 2&#xff1a; 输入&#xff1a;[[…

搭建java开发环境_JAVA 基础:JAVA开发环境搭建

一、在windows上安装jdk下载(略&#xff0c;自行到oracle官网上下载&#xff0c;注意所选系统的版本)2.安装双击jdk安装包以后进行JDK的安装(1)双击进行安装界面如下所示&#xff1a;(2)点击下一步&#xff0c;选择更改jdk的安装路径(一般不推荐安装在系统盘&#xff0c;根据个…

KMP算法——字符串模式匹配

写在前面 有这样一类问题&#xff1a;在主串T中找到子串&#xff08;模式串&#xff09;P的起始位置&#xff0c;那么暴力算法的思想非常简单 暴力算法思想&#xff1a; 从主串T的开头&#xff08;T[i]&#xff09;进行匹配&#xff0c;若遇到不匹配的字符&#xff0c;则移动到…