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