仿Muduo库实现高并发服务器——Buffer模块

        Buffer模块建议描述图。

         关于Buffer模块主要就是信息的读取写入。

这个模块在Connect模块中使用,作为输入输出缓冲区进行使用。

以下是这个模块在本项目中的作用。

写入:

        涉及到是否有足够大小的存储空间。

        //确保可写空间足够(整体空闲空间够了就移动数据,否则就扩容)void EnsureWriteSpace(uint64_t len) {//如果末尾空闲空间大小足够,直接返回if (TailIdleSize() >= len) { return; }//末尾空闲空间不够,则判断加上起始位置的空闲空间大小是否足够, 够了就将数据移动到起始位置if (len <= TailIdleSize() + HeadIdleSize()) {//将数据移动到起始位置uint64_t rsz = ReadAbleSize();//把当前数据大小先保存起来std::copy(ReadPosition(), ReadPosition() + rsz, Begin());//把可读数据拷贝到起始位置_reader_idx = 0;    //将读偏移归0_writer_idx = rsz;  //将写位置置为可读数据大小, 因为当前的可读数据大小就是写偏移量}else {//总体空间不够,则需要扩容,不移动数据,直接给写偏移之后扩容足够空间即可DBG_LOG("RESIZE %ld", _writer_idx + len);_buffer.resize(_writer_idx + len);}}

        判断尾部空闲空间是否满足存储需求,如果满足,那就存储在尾部空闲空间中;

        如果不满足,再看起始空闲和末尾空间 加起来是否满足存储需求,如果满足就将现有数据,挪动到起始位置,将要存储的数据,以写偏移为其实位置进行写入;

        如果起始空闲空间和末尾空闲空间都不满足,那就直接扩容,不用再挪动数据。

以上写入操作对空间是否充足的判断,都是将要存入的数据写到现有数据的后边。

        //将写偏移向后移动 void MoveWriteOffset(uint64_t len) {//向后移动的大小,必须小于当前后边的空闲空间大小assert(len <= TailIdleSize());_writer_idx += len;}

存入数据之后,必须将写偏移向后移动,以确保能将存储的数据检测到,否则下一次写入操作有可能会覆盖之前的写入内容。

        //写入数据void Write(const void *data, uint64_t len) {//1. 保证有足够空间,2. 拷贝数据进去if (len == 0) return;EnsureWriteSpace(len);const char *d = (const char *)data;std::copy(d, d + len, WritePosition());}void WriteAndPush(const void *data, uint64_t len) {Write(data, len);MoveWriteOffset(len);}void WriteString(const std::string &data) {return Write(data.c_str(), data.size());}void WriteStringAndPush(const std::string &data) {WriteString(data);MoveWriteOffset(data.size());}void WriteBuffer(Buffer &data) {return Write(data.ReadPosition(), data.ReadAbleSize());}void WriteBufferAndPush(Buffer &data) { WriteBuffer(data);MoveWriteOffset(data.ReadAbleSize());}

向Buffer中写入分为三种数据类型:char*,string,Buffer。

需要对以上三种类型做出相应的处理,将内容存储到Buffer中。 

读取:

        //将读偏移向后移动void MoveReadOffset(uint64_t len) { if (len == 0) return; //向后移动的大小,必须小于可读数据大小assert(len <= ReadAbleSize());_reader_idx += len;}

读取操作之后也需要对读偏移进行移动,否则下一次读取内容,还是上一次的读取内容。

        //读取数据void Read(void *buf, uint64_t len) {//要求要获取的数据大小必须小于可读数据大小assert(len <= ReadAbleSize());std::copy(ReadPosition(), ReadPosition() + len, (char*)buf);}void ReadAndPop(void *buf, uint64_t len) {Read(buf, len);MoveReadOffset(len);}std::string ReadAsString(uint64_t len) {//要求要获取的数据大小必须小于可读数据大小assert(len <= ReadAbleSize());std::string str;str.resize(len);Read(&str[0], len);return str;}std::string ReadAsStringAndPop(uint64_t len) {assert(len <= ReadAbleSize());std::string str = ReadAsString(len);MoveReadOffset(len);return str;}

        外界通过传入容器的地址来获取数据,需要将内容拷贝到容器中。

        外界传入字符串长度,获取对应的内容,将内容转化为string类型的数据返回,但这些数据是存储在临时空间中的。

        上面两种读取操作都有,读取并删除操作,这种情况就是真的讲内容读取了,如果只进行读取,不会将读偏移向后移动,不会进行删除。需要外界进行读偏移移动。 

 获取一行数据:

        获取数据,我们肯定是获取一行数据。(后续模块中会讲,如果不足一行怎么办等问题)。

        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;}

        我们通过规定以'\n'为分隔符,将一行一行内容进行分割,确保内容之间不会产生影响。 

清除Buffer中的数据内容:

        //清空缓冲区void Clear() {//只需要将偏移量归0即可_reader_idx = 0;_writer_idx = 0;}

只需将读偏移和写偏移全部置零,就可以了。之后写入操作,会将那些内容进行覆盖,不会产生影响。

这里有个问题:

        为什么读取一行数据,直接使用清除Buffer操作,将读写偏移指针进行重置,何必使用读偏移?

        如果客户端连续发送数据,服务端对发送来的数据进行处理,服务端在处理每一条内容时,需要时间,在这个时间期间,客户端有可能会发送很多内容,而这些内容都将存储在服务端中,以便服务端后续再进行处理。

        如果使用清楚操作,客户端发送来的许多请求服务端来不及处理,就会被清除,而无法处理,这是有问题的。 

剩下的简单操作就不再去解释。

以下就是Buffer模块的整体代码:

#define BUFFER_DEFAULT_SIZE 1024
class Buffer {private:std::vector<char> _buffer; //使用vector进行内存空间管理uint64_t _reader_idx; //读偏移uint64_t _writer_idx; //写偏移public:Buffer():_reader_idx(0), _writer_idx(0), _buffer(BUFFER_DEFAULT_SIZE){}char *Begin() { return &*_buffer.begin(); }//获取当前写入起始地址, _buffer的空间起始地址,加上写偏移量char *WritePosition() { return Begin() + _writer_idx; }//获取当前读取起始地址char *ReadPosition() { return Begin() + _reader_idx; }//获取缓冲区末尾空闲空间大小--写偏移之后的空闲空间, 总体空间大小减去写偏移uint64_t TailIdleSize() { return _buffer.size() - _writer_idx; }//获取缓冲区起始空闲空间大小--读偏移之前的空闲空间uint64_t HeadIdleSize() { return _reader_idx; }//获取可读数据大小 = 写偏移 - 读偏移uint64_t ReadAbleSize() { return _writer_idx - _reader_idx; }//将读偏移向后移动void MoveReadOffset(uint64_t len) { if (len == 0) return; //向后移动的大小,必须小于可读数据大小assert(len <= ReadAbleSize());_reader_idx += len;}//将写偏移向后移动 void MoveWriteOffset(uint64_t len) {//向后移动的大小,必须小于当前后边的空闲空间大小assert(len <= TailIdleSize());_writer_idx += len;}//确保可写空间足够(整体空闲空间够了就移动数据,否则就扩容)void EnsureWriteSpace(uint64_t len) {//如果末尾空闲空间大小足够,直接返回if (TailIdleSize() >= len) { return; }//末尾空闲空间不够,则判断加上起始位置的空闲空间大小是否足够, 够了就将数据移动到起始位置if (len <= TailIdleSize() + HeadIdleSize()) {//将数据移动到起始位置uint64_t rsz = ReadAbleSize();//把当前数据大小先保存起来std::copy(ReadPosition(), ReadPosition() + rsz, Begin());//把可读数据拷贝到起始位置_reader_idx = 0;    //将读偏移归0_writer_idx = rsz;  //将写位置置为可读数据大小, 因为当前的可读数据大小就是写偏移量}else {//总体空间不够,则需要扩容,不移动数据,直接给写偏移之后扩容足够空间即可DBG_LOG("RESIZE %ld", _writer_idx + len);_buffer.resize(_writer_idx + len);}} //写入数据void Write(const void *data, uint64_t len) {//1. 保证有足够空间,2. 拷贝数据进去if (len == 0) return;EnsureWriteSpace(len);const char *d = (const char *)data;std::copy(d, d + len, WritePosition());}void WriteAndPush(const void *data, uint64_t len) {Write(data, len);MoveWriteOffset(len);}void WriteString(const std::string &data) {return Write(data.c_str(), data.size());}void WriteStringAndPush(const std::string &data) {WriteString(data);MoveWriteOffset(data.size());}void WriteBuffer(Buffer &data) {return Write(data.ReadPosition(), data.ReadAbleSize());}void WriteBufferAndPush(Buffer &data) { WriteBuffer(data);MoveWriteOffset(data.ReadAbleSize());}//读取数据void Read(void *buf, uint64_t len) {//要求要获取的数据大小必须小于可读数据大小assert(len <= ReadAbleSize());std::copy(ReadPosition(), ReadPosition() + len, (char*)buf);}void ReadAndPop(void *buf, uint64_t len) {Read(buf, len);MoveReadOffset(len);}std::string ReadAsString(uint64_t len) {//要求要获取的数据大小必须小于可读数据大小assert(len <= ReadAbleSize());std::string str;str.resize(len);Read(&str[0], len);return str;}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() {//只需要将偏移量归0即可_reader_idx = 0;_writer_idx = 0;}
};

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

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

相关文章

FASTSPEECH 2论文阅读

FASTSPEECH 2: FAST AND HIGH-QUALITY END-TOEND TEXT TO SPEECH 现状 非自回归模型可以在质量相当的情况下显著快于先前的自回归模型合成模型。但FastSpeech模型训练依赖与自回归教师模型进行时长预测&#xff08;提供更多的信息作为输入&#xff09;和知识蒸馏&#xff08;…

SEREN赛恩电源RX01/LX01系列射频手侧

SEREN赛恩电源RX01/LX01系列射频手侧

C:数组传参的本质

1、一维数组传参的本质 数组传参是指在函数调用时将数组作为参数传递给函数。 int main() {int arr[10] { 1,2,3,4,5,6,7,8,9,10 };test(arr);return 0;}数组传参只需要写数组名就可以了。注意&#xff1a;数组名是arr&#xff0c;而不是arr[10] 数组传参形参该怎么写呢&am…

Golang Map 深度剖析:原理、实践与面试要点

嘿,小伙伴们!我是 k 哥。今天,咱们来聊聊 Map 。 在 Go 语言这个神奇的世界里,Map 这个有点神秘的数据结构一直都是开发者们特别关注的。 你是不是在用 Map 的时候,对它里面咋工作的感到好奇?是不是碰到复杂操作的时候,特别想弄明白它背后的原理?别着急,今天这篇文章…

【计算机网络】应用层自定义协议与序列化

记得在上一节我们说过TCP中的读取时需要改进&#xff0c;这节就可以解决读取问题了。 目录 应用层再谈 "协议"网络版计算机方案一方案二 序列化 和 反序列化 重新理解 read、write、recv、send 和 tcp 为什么支持全双工 应用层 再谈 “协议” 我们在UDP与TCP中写的…

支持I2C接口、抗干扰性强、14通道触摸按键的电容式触摸芯片-GTX314L

电容式触摸芯片 - GTX314L是具有多通道触发传感器的14位触摸传感器系列&#xff0c;它是通过持续模式提供中断功能和唤醒功能&#xff0c;广泛适用于各种控制面板应用&#xff0c;可直接兼容原机械式轻触按键的处理信号。 GTX314L芯片内部采用特殊的集成电路&#xff0c;具有高…

基于YOLOv8-pose的手部关键点检测(3)- 实现实时手部关键点检测

目录 前言 1.扩大检测框区域 2.先检测手部&#xff0c;后检测手部关键点 3.正面视角检测 4.侧面视角检测 5.摄像头视角检测 6.遮挡视角检测 7.结论 前言 使用YOLOv8-m对图像进行手部检测&#xff0c;然后扩大检测框区域&#xff0c;并对该区域使用YOLOv8-s-pose使用关键…

大数据技术——实战项目:广告数仓(第六部分)报表数据导出至clickhouse

目录 第11章 报表数据导出 11.1 Clickhouse安装 11.2 Clickhouse建表 11.2.1 创建database 11.2.2 创建table 11.3 Hive数据导出至Clickhouse 第11章 报表数据导出 由于本项目最终要出的报表&#xff0c;要求具备交互功能&#xff0c;以及进行自助分析的能力&#xff0c;…

CSS——字体背景(Font Background)

一、字体族 1、字体的相关样式&#xff1a; ① color 用来设置字体颜色&#xff08;前景颜色&#xff09; ② font-size 字体的大小 和font-size相关的单位&#xff1a; em 相对于当前元素的一个font-size rem 相对于根元素的一个font-size ③ font-family 字体族&#x…

命令行参数环境变量

目录 前言&#xff1a; 命令行参数&#xff1a; 现象&#xff1a; 这些参数的意义&#xff1a; 为什么要这么做&#xff1f; 这些事是谁做的呢&#xff1f; 环境变量 现象&#xff1a; 创建环境变量&#xff1a; 结合程序理解&#xff1a; 前言&#xff1a; 我们在前…

1.Linux_常识

UNIX、Linux、GNU 1、UNIX UNIX是一个分时操作系统&#xff0c;特点是多用户、多任务 实时操作系统&#xff1a;来了请求就去解决请求 分时操作系统&#xff1a;来了请求先存着&#xff0c;通过调度轮到执行时执行 2、Linux Linux是一个操作系统内核 发行版本&#xff1…

C++(10)类语法分析(1)

C(10)之类语法分析(1) Author: Once Day Date: 2024年8月17日 一位热衷于Linux学习和开发的菜鸟&#xff0c;试图谱写一场冒险之旅&#xff0c;也许终点只是一场白日梦… 漫漫长路&#xff0c;有人对你微笑过嘛… 全系列文章可参考专栏: 源码分析_Once-Day的博客-CSDN博客 …

基于单片机的智能晾衣系统设计

摘 要 &#xff1a;在网络信息技术的推动下&#xff0c;智能家居得到了广泛应用&#xff0c;文章根据当前的市场动态&#xff0c;针对基于单片机的智能晾衣系统设计展开论述&#xff0c;具体包括两个方面的内容———硬件设计和软件设计。 关键词 &#xff1a;单片机&#xff…

【数据结构篇】~顺序表

顺序表前言 想要学好数据结构的三大基本功&#xff1a;1.结构体2.指针3.动态内存开辟,这三样将是贯彻整个数据结构的工具。&#xff08;可以去这里了解这三大基本功&#xff09; 顺序表也是线性表的一种&#xff0c;那线性表又是什么呢&#xff1f; 线性表&#xff08;linear …

应急响应-DDOS-技术指南

初步预判 通常&#xff0c;可从以下几方面判断服务器/主机是否遭受DDoS攻击查看防火墙、流量监控设备、网络设备等是否出现安全告警或大量异常数据包。如图所示&#xff0c;通过流量对比&#xff0c;发现在异常时间段存在大量UDP数据包&#xff0c;并且与业务无关。 通过安全设…

uniapp多图上传uni.chooseImage上传照片uni.uploadFile,默认上传9张图

uniapp多图上传uni.chooseImage上传照片uni.uploadFile 代码示例&#xff1a; /**上传照片 多图*/getImage() {uni.chooseImage({count: 9, //默认9sizeType: [original, compressed], //可以指定是原图还是压缩图&#xff0c;默认二者都有sourceType: [album], //从相册选择/…

8 Java常用API(基本语法6)-- Object和Objects类、Math、System、浅克隆和深克隆、手动下载导入第三方jar包

文章目录 前言一、Math(工具类)1 属性2 常见方法二、System(工具类,和系统相关的)1 public static void exit(int status) --- 终止当前运行的 Java 虚拟机。2 public static long currentTimeMillis() --- 以毫秒为单位返回当前unix时间。3 public static void arraycopy(Obj…

在Windows上配置VSCode MinGW+CMake(包括C++多线程编程的两套API:posix和win32)

创建目录 首先&#xff0c;需要电脑上安装VSCode, 并且创建三个文件夹&#xff1a;cmake、MinGW-posix、MinGW-w32 文件下载 下载posix-seh posix和win32分别是c多线程变成的两套API,可根据不同需求安装&#xff0c;现在先下载配置环境需要的几个文件 百度搜索MinGW-64 点…

Apache--简介与基本使用

前言&#xff1a;本博客仅作记录学习使用&#xff0c;部分图片出自网络&#xff0c;如有侵犯您的权益&#xff0c;请联系删除 一、Apache简介 Apache HTTP Server&#xff08;在Red Hat发行版中俗称Apache或httpd&#xff09;是由Apache Software Foundation在Apache License…

WPF打印控件内容

当我们想打印控件内容时&#xff0c;如一个Grid中的内容&#xff0c;可以用WPF中PrintDialog类的PrintVisual()方法来实现 界面如下&#xff1a; XAML代码如下 <Grid><Grid.ColumnDefinitions><ColumnDefinition/><ColumnDefinition Width"300"…