仿Muduo高并发服务器之Buffer模块

Buffer模块是⼀个缓冲区模块,用于实现通信中用户态的接收缓冲区和发送缓冲区功能。

设计方式:

|-------------------------------|
| * 数据存储在vector中,通过读写指针管理 |
| * 智能空间管理,通过移动数据和扩容的方式来是数据安全存储 |

优点:

|-------------------|
| * 自动处理数据移动和空间扩展 |
| * 避免了环形缓冲区的复杂边界判断 |
| * 一次读取尽可能多数据 |

主要设计步骤:

通过一个数组加两个指针偏移量来维护,数组表示存储数据的缓存区,读写偏移量来记录读写缓存区的起始位置,当需要读取数据时只需要从读偏移量开始位置读,自定义读取字节数,然后存入传入指针;写数据和读数据方式类似,先检查缓冲区写空间的大小,缓存区扩容空间规则是优先使用尾部空余空间,如果空间不够,就去检查前后加起来的空间够不够,够的话把数据往前移,不够则从新开辟空间,然后有充足的空间之后再来写入数据。

下面是具体代码实现:

cpp 复制代码
//通过一个数组加两个指针偏移量来维护,数组表示存储数据的缓存区,读写偏移量来记录读写缓存区的最后一个位置
#define BUFFER_DEFAULT_SIZE 1024 // 定义缓冲区默认大小为 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;
        }
};
相关推荐
Tony Bai1 分钟前
高并发后端:坚守 Go,还是拥抱 Rust?
开发语言·后端·golang·rust
wjs202422 分钟前
Swift 类型转换
开发语言
前端小L41 分钟前
贪心算法专题(十):维度权衡的艺术——「根据身高重建队列」
javascript·算法·贪心算法
方得一笔42 分钟前
自定义常用的字符串函数(strlen,strcpy,strcmp,strcat)
算法
秃了也弱了。43 分钟前
python实现定时任务:schedule库、APScheduler库
开发语言·python
weixin_440730501 小时前
java数组整理笔记
java·开发语言·笔记
Xの哲學1 小时前
Linux SMP 实现机制深度剖析
linux·服务器·网络·算法·边缘计算
Thera7771 小时前
状态机(State Machine)详解:原理、优缺点与 C++ 实战示例
开发语言·c++
linux开发之路1 小时前
C++高性能日志库开发实践
c++·c++项目·后端开发·c++新特性·c++校招
wuk9982 小时前
使用PCA算法进行故障诊断的MATLAB仿真
算法·matlab