C++仿mudo库高并发服务器项目:Buffer模块
前言
本篇文章所讲的是本人的个人项目仿muduo库高并发服务器中的Buffer模块实现部分。
Buffer模块介绍:
Buffer类⽤于实现⽤⼾态缓冲区,提供数据缓冲,取出等功能。
-
功能:用于实现通信套接字的用户态缓冲区
-
意义:
- 防止接收到的数据不是一条完整的数据(提示:TCP面向字节流),因此对接收的数据进行缓冲
- 对客户端响应的数据,应该是在套接字可写的情况下进行发送
-
功能设计:
- 向缓冲区中添加数据
- 从缓冲区中取出数据
实现思路:
-
实现缓冲区得有一块内存空间,采用vector,vector底层其实使用的就是一个线性的内存空间。
这里不使用string作为容器是因为,缓冲器要存储的数据很大可能会包含字符0等字符,而string的方法往往会将它识别为数据结尾
-
要素:
-
默认的空间大小。
-
当前的读取数据位置。
-
当前的写入数据位置。
-
记录当前读取位置和当前写入位置是因为,在数据被读走时我们不需要移动数据,只需要更新下一次读取位置即可,插入时同理。

写入操作:
当前写入位置在哪,就在哪写,当待写入空间大小,无法满足接下来的写入需求,这时我们就面临两种选择:
- 待写入数据所需空间大小<待写入空间+可覆盖空间,这种情况下我们只需要移动数据到容器开始处,再写入待写入数据即可。
- 待写入数据所需空间大小>待写入空间+可覆盖空间,这种情况下我们只能选择对缓存空间扩容。
读取操作:
当前读取位指向哪里,就从哪里开始读取,不过前提是得有数据可读;而可读数据的大小,是用当前写入位置减去当前读取位置来计算的。
代码实现:
const uint64_t BUFFER_DEFAULT_SIZE = 1024;
class Buffer
{
private:std::vector<char> _buffer;//使用vector进行内存空间管理uint64_t _reader_idx;//读偏移uint64_t _write_idx;//写偏移public:Buffer():_reader_idx(0),_write_idx(0),_buffer(BUFFER_DEFAULT_SIZE){}char* Begin(){ return &*_buffer.begin(); }//获取当前写入起始地址char* WritePosition(){return Begin() + _write_idx;}//获取当前读取起始地址char* ReadPosition(){return Begin() + _reader_idx;}//获取缓冲区末尾空闲空间大小uint64_t TailIdleSize(){return _buffer.size() - _write_idx;}//获取缓冲区起始空闲空间大小uint64_t HeadIdleSize(){return _reader_idx;}//获取可读数据大小uint64_t ReadAbleSize(){return _write_idx - _reader_idx;}//将读偏移向后移动void MoveReadOffset(uint64_t len){if(len == 0) return;//必须小于可读空间大小assert(ReadAbleSize() >= len);_reader_idx += len;}//将写偏移向后移动void MoveWriteOffset(uint64_t len){//必须小于尾部空间大小assert(TailIdleSize() >= len);_write_idx += len;}//确保可写空间足够lenvoid EnsureWriteSpace(uint64_t len){//尾部空间够,直接返回if(TailIdleSize() >= len) return;//总体空间够,将数据平移到最开始,为后面腾位置if(TailIdleSize() + HeadIdleSize() >= len){//保存一下可读数据大小uint64_t rsz = ReadAbleSize();std::copy(ReadPosition(),ReadPosition() + rsz,Begin());//可读数据拷贝到起始位置_reader_idx = 0;_write_idx = rsz;}//不够,扩容else{_buffer.resize(_write_idx + len);}}//写入数据void Write(const void* date,uint64_t len){//保证空间足够if(len == 0) return;EnsureWriteSpace(len);//将数据拷贝进去const char* d = (const char*)date;std::copy(d,d + len,WritePosition());}//写入数据并且更新写指针void WriteAndPush(const void* date,uint64_t len){Write(date,len);MoveWriteOffset(len);}//写入一个字符串void WriteString(const std::string& date){Write(date.c_str(),date.size());}//写入一个字符串并且更新写指针void WriteStringAndPush(const std::string& date){WriteString(date);MoveWriteOffset(date.size());}//写入一个Buffervoid WriteBuffer(Buffer& date){Write(date.ReadPosition(),date.ReadAbleSize());}//写入一个Buffer并且更新写指针void WriteBufferAndPush(Buffer& date){Write(date.ReadPosition(),date.ReadAbleSize());MoveWriteOffset(date.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);}//读取一个长度为len的字符串std::string ReadString(uint64_t len){assert(len <= ReadAbleSize());std::string str;str.resize(len);Read(&str[0],len);return str;}//读取一个长度为len的字符串并且更新读指针std::string ReadStringAndPop(uint64_t len){assert(len <= ReadAbleSize());std::string str = ReadString(len);MoveReadOffset(len);return str;}//查找换行符char* FindCRLF(){char* res = (char*)memchr(ReadPosition(),'\n',ReadAbleSize());return res;}//获取一行字符串std::string GetLine(){//找到换行符位置char* pos = FindCRLF();if(pos == nullptr) return "";//+1是为了把换行符也取出来return ReadString(pos - ReadPosition() + 1);}//获取一行字符串并且更新读指针std::string GetLineAndPop(){std::string str = GetLine();MoveReadOffset(str.size());return str;}//清空缓冲区void Clear(){//偏移量归零_reader_idx = _write_idx = 0;}
};
测试代码:
#include "Buffer.hpp"int main()
{Buffer buf;for(int i=0;i<300;i++){std::string ret = "hello linux "+std::to_string(i)+'\n';buf.WriteStringAndPush(ret);}while(buf.ReadAbleSize()>0){std::cout << buf.GetLineAndPop();}std::cout << buf.ReadAbleSize() << std::endl;std::string str1;str1 = buf.ReadStringAndPop(buf.ReadAbleSize());std::cout << str1 << std::endl;Buffer buf0;std::string str2="hello linux !!";buf0.WriteStringAndPush(str2);std::cout << buf0.ReadAbleSize() << std::endl;Buffer buf1;buf1.WriteBufferAndPush(buf0);std::cout << buf1.ReadAbleSize() << std::endl;return 0;
}
测试结果:

