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

写入操作:
当前写入位置在哪,就在哪写,当待写入空间大小,无法满足接下来的写入需求,这时我们就面临两种选择:
- 待写入数据所需空间大小<待写入空间+可覆盖空间,这种情况下我们只需要移动数据到容器开始处,再写入待写入数据即可。
- 待写入数据所需空间大小>待写入空间+可覆盖空间,这种情况下我们只能选择对缓存空间扩容。
读取操作:
当前读取位指向哪里,就从哪里开始读取,不过前提是得有数据可读;而可读数据的大小,是用当前写入位置减去当前读取位置来计算的。
代码实现:
c++
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;
}
//确保可写空间足够len
void 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());
}
//写入一个Buffer
void 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;
}
};
测试代码:
c++
#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;
}
测试结果:
