1.4.C++项目:仿muduo库实现并发服务器之buffer模块的设计

项目完整版在:

一、buffer模块: 缓冲区模块

在这里插入图片描述
Buffer模块是一个缓冲区模块,用于实现通信中用户态的接收缓冲区和发送缓冲区功能。

二、提供的功能

存储数据,取出数据

三、实现思想

1.实现换出去得有一块内存空间,采用vector ,vector底层是一个线性的内存空间!

(一)要素

1.默认空间大小
2.当前的读取数据位置!
3.当前的写入数据位置!

(二)操作

  1. 写入位置
    当前写入位置指向哪里,从哪里开始写入
    如果后续剩余空间不够了!
  2. 考虑整体缓冲区空闲空间是否足够!(因为读位置也会向后偏移,前后有可能有空闲空间)
    足够:将数据移动到起始位置
    不够:扩容,从当前写位置开始扩容足够大小!
    数据一旦写入成功,当前写位置,向后偏移!
  3. 读取数据
    当前的读取位置指向哪里,就从哪里开始读取,前提是有数据可读
    可读数据大小:当前写入位置,减去当前读取位置!

(三)框架设计

class buffer {private:std::vector<char> _buffer;// 位置是一个相对偏移量,而不是绝对地址!uint64_t _read_idx; // 读位置uint64_t _write_idx; // 写位置public:1. 获取当前写的位置2. 确保可写空间足够3. 获取前沿空间大小4. 获取后沿空间大小5. 将写入据位置向后移动指定长度6. 获取当前读取位置的地址!7. 获取可读空间大小8. 将读位置向后移动指定长度!9. clear

四、实现代码

#include <ctime>
#include <cstring>
#include <iostream>
#include <vector>
#include <cassert>
#include <string>
using namespace std;
#define BUFFER_SIZE 1024
class Buffer {private:std::vector<char> _buffer; // 使用vector进行内存空间管理uint64_t _read_idx; // 读偏移uint64_t _write_idx; // 写偏移public:Buffer():_read_idx(0),_write_idx(0),_buffer(BUFFER_SIZE) {}char* begin() {return &*_buffer.begin();}// 获取当前写入起始地址char *writePosition() { return begin() + _write_idx;}// 获取当前读取起始地址char *readPosition() { return begin() + _read_idx; }// 获取缓冲区末尾空间大小 —— 写偏移之后的空闲空间,总体大小减去写偏移uint64_t tailIdleSize() {return _buffer.size() - _write_idx; }// 获取缓冲区起始空间大小 —— 读偏移之前的空闲空间uint64_t handIdleSize() {return _read_idx ;}// 获取可读空间大小 = 写偏移 - 读偏移 uint64_t readAbleSize() {return _write_idx - _read_idx ;} // 将读偏移向后移动void moveReadOffset(uint64_t len) { // 向后移动大小必须小于可读数据大小assert(len <= readAbleSize());_read_idx += len; }// 将写偏移向后移动void moveWriteOffset(uint64_t len) { assert(len <= tailIdleSize());_write_idx += len;}void ensureWriteSpace(uint64_t len)  {// 确保可写空间足够 (整体空间够了就移动数据,否则就扩容!)  if (tailIdleSize() >= len) return;// 不够的话 ,判断加上起始位置够不够,够了将数据移动到起始位置if (len <= tailIdleSize() + handIdleSize()) {uint64_t rsz = readAbleSize(); //帮当前数据大小先保存起来std::copy(readPosition(),readPosition() + rsz,begin()); // 把可读数据拷贝到起始位置_read_idx = 0; // 读归为0_write_idx = rsz; // 可读数据大小是写的偏移量!}else { // 总体空间不够!需要扩容,不移动数据,直接给写偏移之后扩容足够空间即可!_buffer.resize(_write_idx + len);}}// 写入数据void Write(const void *data,uint64_t len) {ensureWriteSpace(len);const char *d = (const char*) data;std::copy(d,d + len,writePosition());}void WriteAndPush(void* data,uint64_t len) {Write(data,len);moveWriteOffset(len);}void WriteStringAndPush(const std::string &data) {writeString(data);moveWriteOffset(data.size());}void writeString(const std::string &data) {return Write(data.c_str(),data.size());}void writeBuffer(Buffer &data) {return Write(data.readPosition(),data.readAbleSize());}void writeBufferAndPush(Buffer &data) {writeBuffer(data);moveWriteOffset(data.readAbleSize());}std::string readAsString (uint64_t len) {assert(len <= readAbleSize());std::string str;str.resize(len);Read(&str[0],len);return str;}void Read(void *buf,uint64_t len) {// 读取数据 1. 保证足够的空间 2.拷贝数据进去// 要求获取的大小必须小于可读数据大小!assert(len <= readAbleSize());std::copy(readPosition(),readPosition() + len,(char*)buf);}void readAndPop(void *buf,uint64_t len) {Read(buf,len);moveReadOffset(len);}// 逐步调试!!!!!std::string ReadAsStringAndPop(uint64_t len) {assert(len <= readAbleSize());std::string str = readAsString(len);moveReadOffset(len);return str;}char* FindCRLF() {char *res = (char*)memchr(readPosition(),'\n',readAbleSize());return res;}// 通常获取一行数据,这种情况针对是:std::string getLine() {char* pos = FindCRLF();if (pos == NULL) {return "";}// +1 为了把换行数据取出来!return readAsString(pos - readPosition() + 1);}std::string getLineAndPop() {std::string str = getLine();moveReadOffset(str.size());return str;}void Clear() { // 清空缓冲区!clear// 只需要将偏移量归0即可!_read_idx = 0;_write_idx = 0;}
};

五、进行测试

#include "server.hpp"
using namespace std;
// 控制打印信息!!!
#define INF 0
#define DBG 1
#define ERR 2
#define LOG_LEVEL INF#define LOG(level,format,...) do{\if (level < LOG_LEVEL) break;\time_t t = time(NULL);\struct tm *ltm = localtime(&t);\char tmp[23] = {0};\strftime(tmp,31,"%H:%M:%S",ltm);\fprintf(stdout,"[%s,%s:%d] " format "\n",tmp,__FILE__,__LINE__,##__VA_ARGS__);\
}while(0)#define INF_LOG(format, ...) LOG(INF, format, ##__VA_ARGS__)
#define DBG_LOG(format, ...) LOG(DBG, format, ##__VA_ARGS__)
#define ERR_LOG(format, ...) LOG(ERR, format, ##__VA_ARGS__)int main() {Buffer buf;std::string str = "hello!";// buf.WriteStringAndPush(str);// Buffer buf1;// buf1.writeBufferAndPush(buf);// std::string tmp;// tmp = buf1.ReadAsStringAndPop(buf.readAbleSize());// cout << tmp << endl;// cout << buf.readAbleSize() << endl;// cout << buf1.readAbleSize() << endl;for (int i = 0; i < 300; i ++) {std::string str = "hello" + std::to_string(i) + '\n';buf.WriteStringAndPush(str);}while(buf.readAbleSize() > 0) {string line = buf.getLineAndPop();LOG("hello");  }// string tmp;// tmp = buf.ReadAsStringAndPop(buf.readAbleSize());// cout << tmp << endl;return 0;
}

中秋快乐!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/92043.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Redis与分布式-集群搭建

接上文 Redis与分布式-哨兵模式 1. 集群搭建 搭建简单的redis集群&#xff0c;创建6个配置&#xff0c;开启集群模式&#xff0c;将之前配置过的redis删除&#xff0c;重新复制6份 针对主节点redis 1&#xff0c;redis 2&#xff0c;redis 3都是以上修改内容&#xff0c;只是…

十、空闲任务及其钩子函数

1、空闲任务的介绍 (1)一个良好的程序&#xff0c;它的任务都是事件驱动的&#xff1a;平时大部分时间处于阻塞状态。 (2)有可能我们自己创建的所有任务都无法执行&#xff0c;但是调度器必须能找到一个可以运行的任务。所以&#xff0c;我们要提供空闲任务。 (3)在使用vTas…

格拉姆角场GAF将时序数据转换为图像并应用于凯斯西楚大学轴承故障诊断(Python代码,CNN模型)

1.运行效果&#xff1a; 格拉姆角场GAF将时序数据转换为图像并应用于故障诊断&#xff08;Python代码&#xff09;_哔哩哔哩_bilibili 环境库 只要tensorflow版本大于等于2.4.0即可运行 2.GAF的内容 GAF是一种用于时间序列数据可视化和特征提取的技术&#xff0c;通常用于…

Linux——补充点(进程切换及页表映射)

目录 补充点1&#xff1a;进程地址空间堆区管理 补充点2&#xff1a;Linux内核进程上下文切换 补充点3&#xff1a;页表映射 补充点4&#xff1a;两级页表 补充点1&#xff1a;进程地址空间堆区管理 Linux内核通过一个被称为进程描述符的task_struct结构体来管理进程&#…

keil调试的时候没问题,下载时候没反应

今天遇到这样一个问题。我下载商家的代码例程后单片机没反应&#xff0c;进入调试的时候一切正常。很奇怪&#xff0c;在网上找了教程问题解决&#xff0c;总结一下。 原因在于程序下载进去后没有按下复位键&#xff0c;导致还是之前的程序。我之前设置的是下载后自动复位运行…

【STL】用一棵红黑树封装map和set

⭐博客主页&#xff1a;️CS semi主页 ⭐欢迎关注&#xff1a;点赞收藏留言 ⭐系列专栏&#xff1a;C进阶 ⭐代码仓库&#xff1a;C进阶 家人们更新不易&#xff0c;你们的点赞和关注对我而言十分重要&#xff0c;友友们麻烦多多点赞&#xff0b;关注&#xff0c;你们的支持是我…

玩转gpgpu-sim 04记—— __cudaRegisterBinary() of gpgpu-sim 到底做了什么

官方文档&#xff1a; GPGPU-Sim 3.x Manual __cudaRegisterBinary(void*) 被执行到的代码逻辑如下&#xff1a; void** CUDARTAPI __cudaRegisterFatBinary( void *fatCubin ) { #if (CUDART_VERSION < 2010)printf("GPGPU-Sim PTX: ERROR ** this version of GPGPU…

小程序如何设置余额充值

在小程序中设置余额充值是一种非常有效的方式&#xff0c;可以帮助商家吸引更多的会员并提高用户的消费频率。下面将介绍如何在小程序中设置余额充值并使用。 第一步&#xff1a;创建充值方案 在小程序管理员后台->营销管理->余额充值页面&#xff0c;添加充值方案。可…

Spark性能监测+集群配置

spark-dashboard 参考链接 架构图 Spark官网中提供了一系列的接口可以查看任务运行时的各种指标 运行 卸载docker https://blog.csdn.net/wangerrong/article/details/126750198 sudo yum remove docker \docker-client \docker-client-latest \docker-common \docker-latest…

Linux系统编程(七):线程同步

参考引用 UNIX 环境高级编程 (第3版)黑马程序员-Linux 系统编程 1. 同步概念 所谓同步&#xff0c;即同时起步、协调一致。不同的对象&#xff0c;对 “同步” 的理解方式略有不同 设备同步&#xff0c;是指在两个设备之间规定一个共同的时间参考数据库同步&#xff0c;是指让…

小白继续深入学习C++

第1节 指针的基本概念 1、变量的地址&#xff1a; 变量是内存地址的简称&#xff0c;在C中&#xff0c;每定义一个变量&#xff0c;系统就会给变量分配一块内存&#xff0c;内存是有地址的。 C用运算符&获取变量在内存中的起始地址。 语法&#xff1a; &变…

零基础Linux_9(进程)环境变量+进程地址空间+进程创建fork

目录 1. 环境变量 1.1 环境变量基本概念 1.2 环境变量PATH 1.3 环境变量HOME和SHELL 1.4 获取环境变量&#xff08;main函数参数&#xff09; 1.4.1 main函数第三个参数 1.4.2 设置普通变量和环境变量 1.4.3 main函数前两个参数 2. 进程地址空间 2.1 验证进程地址空…

PHP8的数据封装(数据隐藏)-PHP8知识详解

面向对象的特点之一就是封装性&#xff0c;也就是数据封装&#xff0c;也被称为数据隐藏。 php8通过限制访问权限来实现数据的封装性&#xff0c;这里用到了public、private、protected、static和final几个关键字。下面来介绍前3个。 1.、public&#xff08;公共成员&#xf…

CSP-J第二轮试题-2021年-3题

文章目录 参考&#xff1a;总结 [CSP-J 2021] 网络连接题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #1 样例 #2样例输入 #2样例输出 #2 样例 #3样例输入 #3样例输出 #3 样例 #4样例输入 #4样例输出 #4 提示答案1答案2 现场真题注意事项 参考&#xff1a; https://www…

2022年全球一次能源消费量:石油消耗量持续增加达190.69百亿亿焦耳,亚太地区消费量居首位[图]

一次性能源是指从自然界取得未经改变或转变而直接利用的能源。如原煤、原油、天然气、水能、风能、太阳能、海洋能、潮汐能、地热能、天然铀矿等。一次性能源又分为可再生能源和不可再生能源&#xff0c;前者指能够重复产生的天然能源&#xff0c;包括太阳能、风能、潮汐能、地…

C/C++与汇编混合编程

1. C/C调用汇编 C/C想调用汇编代码必须要注意名称修饰的问题 名称修饰(name decoration): 一种标准的C/C编译技术, 通过添加字符来修改函数名, 添加的字符指明了每个函数参数的确切类型。主要是为了支持函数重载, 但对于汇编来说其问题在于, C/C编译器让链接器去找被修饰过的名…

cadence SPB17.4 S032 - 使用room来放置元件

文章目录 cadence SPB17.4 S032 - 使用room来放置元件概述笔记在orcad中设置子原理图的ROOM号码在空的Allegro工程中, 放入板框在allegro中建立room备注补充 - ROOM还得留着END cadence SPB17.4 S032 - 使用room来放置元件 概述 如果在allegro中直接手工或自动放置元件, 放好…

LeNet网络复现

文章目录 1. LeNet历史背景1.1 早期神经网络的挑战1.2 LeNet的诞生背景 2. LeNet详细结构2.1 总览2.2 卷积层与其特点2.3 子采样层&#xff08;池化层&#xff09;2.4 全连接层2.5 输出层及激活函数 3. LeNet实战复现3.1 模型搭建model.py3.2 训练模型train.py3.3 测试模型test…

Linux系统编程系列之进程间通信-信号量组

一、什么是信号量组 信号量组是信号量的一种&#xff0c; 是system-V三种IPC对象之一&#xff0c;是进程间通信的一种方式。 二、信号量组的特性 信号量组不是用来传输数据的&#xff0c;而是作为“旗语”&#xff0c;用来协调各进程或者线程工作的。信号量组可以一次性在其内…

【LeetCode】滑动窗口妙解无重复字符的最长子串

Problem: 3. 无重复字符的最长子串 文章目录 思路算法原理分析暴力枚举 哈希表滑动窗口 复杂度Code 思路 首先我们来分析一下本题的思路 如果读者有看过 长度最小的子数组 的话就可以清楚这个子串其实和子数组是一个道理&#xff0c;都是 连续的一段区间但是呢它们本质上还是存…