环形缓冲区设计分两种模式
来源:微信公众号「编程学习基地」文章目录
模式一
写入读取数据,不考虑读取数据的长度,读取数据的顺序为写入数据的顺序
环形缓冲区测试代码
#include "lwsBuffer.h"
#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main()
{
char dataBuf[1024];
void *temp = NULL;
int readFd = open("lwsBuffer.cpp", O_RDONLY);
int writeFd = open("temp.cpp", O_WRONLY | O_CREAT);
int ret;
memset(dataBuf, '\0', 1024);
lwsBuffer Buff;
while (1)
{
/* code */
memset(dataBuf, '\0', sizeof(dataBuf));
ret = read(readFd, dataBuf, sizeof(dataBuf));
if (ret == 0)
{
printf("read end..");
break;
}
else if (ret < 0)
printf("read error ret:%d", ret);
else
printf("read ret:%d\n", ret);
ret = Buff.write(dataBuf, ret); //将 dataBuf 数据写入到Buff缓冲区
printf("write ret:%d\n", ret);
}
printf("\n\ncurrent Buff total len:%d\n\n", Buff.getTotalLen());
while (1)
{
char tempBuf[1024];
memset(tempBuf,'\0',sizeof(tempBuf));
ret = Buff.read(&temp); //从 Buff缓冲区中读取数据
if (ret == -1)
break;
memmove(tempBuf, temp, ret);
/* code */
printf("total len:%d\n", Buff.getTotalLen());
write(writeFd, tempBuf, ret);
}
close(readFd);
close(writeFd);
return 0;
}
makefile编译文件
test:test.cpp
g++ -o test test.cpp lwsBuffer.cpp
.PHONY:clean
clean:
rm -f test temp.cpp
运行结果
sh-4.3$ make
g++ -o test test.cpp lwsBuffer.cpp
sh-4.3$ ls
lwsBuffer.cpp lwsBuffer.h makefile test test.cpp
sh-4.3$ ./test
read ret:1024
write ret:1024
read ret:1024
write ret:1024
read ret:1024
write ret:1024
read ret:964
write ret:964
read end..
current Buff total len:4036
total len:3012
total len:1988
total len:964
total len:0
sh-4.3$ ls
lwsBuffer.cpp lwsBuffer.h makefile temp.cpp test test.cpp
执行后结果就是读取 lwsBuffer.cpp
里面的数据,储存到环形缓冲区,再从环形缓冲区读取数据写入到 temp.cpp
应用场景和优缺点
大量数据的转发
优点:牺牲少量内存实现最少的数据拷贝memmove,极大的提高服务性能。理论上每个数据块只需要一次拷贝。
缺点:当转发的数据很大时,没一个对象浪费的内存在转发的数据大小之内
当读取数据缓慢的时候会造成频繁的内存重分配,缓冲区变得越来越大。
lwsBuffer.h
#pragma once
#ifndef _LWS_BUFFER_H_
#define _LWS_BUFFER_H_
#include <iostream>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <list>
#define BUFFER_SIZE 1024
class lwsBuffer
{
private:
/* data */
void* buffer; /* 数据 */
size_t bufMaxSize; /* 最大的存储数据大小 */
size_t rightWritePos;
int readpos,writepos; /* 读写位置 */
size_t totalLen; /* 存储的数据大小 */
std::list<int> lenList; /* 数据长度的链表 */
public:
lwsBuffer(/* args */);
~lwsBuffer();
public:
int write(void *data, int dataLen);
int read(void** data);
int getTotalLen();
protected:
int writeAfter(void* data, int dataLen);
int writePre(void* data, int dataLen);
void _remalloc();
};
#endif
lwsBuffer.cpp
#include "lwsBuffer.h"
lwsBuffer::lwsBuffer(/* args */)
{
this->readpos = 0;
this->writepos = 0;
this->rightWritePos = 0;
this->bufMaxSize = BUFFER_SIZE;
this->buffer = malloc(this->bufMaxSize);
this->totalLen = 0;
this->lenList.clear();
}
lwsBuffer::~lwsBuffer()
{
if (buffer)
free(buffer);
}
int lwsBuffer::write(void *data, int dataLen)
{
int ret = -1;
if (dataLen <= 0 || data == NULL)
return ret;
/* writePos 在 readPos 前 / readPos == writePos push*/
if (this->writepos >= this->readpos)
{
ret = writePre(data, dataLen);
}
/* readPos 在 writePos 前 push*/
else if(this->writepos < this->readpos)
{
ret = writeAfter(data, dataLen);
}
return ret;
}
int lwsBuffer::writePre(void* data, int dataLen)
{
int ret = -1;
/* 写入位置在前 */
/* buffer 后面有空间可以写入数据*/
if ((this->writepos + dataLen) <= this->bufMaxSize)
{
//将数据写入到 buffer 里面去
memmove((char *)this->buffer + this->writepos, data, dataLen);
/* 修改 writepos 偏移 */
this->writepos = this->writepos + dataLen;
/* 计算数据长度*/
lenList.push_back(dataLen);
totalLen += dataLen;
ret = dataLen;
}
else
{
/* buffer 后面没有空间可以写入数据 记录下罪*/
this->rightWritePos = this->writepos;
/* 修改 writepos 偏移 */
this->writepos = 0;
ret = writeAfter(data, dataLen);
}
return ret;
}
int lwsBuffer::writeAfter(void *data, int dataLen)
{
int ret = -1;
/* 写入位置在后 */
/* writepos 到 readpos 有足够的空间可以写入数据 */
if ((this->writepos + dataLen) < this->readpos)
{
//将数据写入到 buffer 里面去
memmove((char *)this->buffer + this->writepos, data, dataLen);
/* 修改 writepos 偏移 */
this->writepos = this->writepos + dataLen;
/* 计算数据长度*/
lenList.push_back(dataLen);
totalLen += dataLen;
ret = dataLen;
}
else
{
this->_remalloc();
ret = writePre(data, dataLen);
}
return ret;
}
void lwsBuffer::_remalloc()
{
/* 数据满了 */
void *newBuf = malloc(this->bufMaxSize + BUFFER_SIZE / 2);
/* */
memmove(newBuf, (char*)this->buffer + this->readpos, this->rightWritePos - this->readpos);
memmove((char*)newBuf + this->rightWritePos - this->readpos, this->buffer, this->writepos);
free(this->buffer);
this->buffer = newBuf;
this->writepos = this->rightWritePos - this->readpos + this->writepos;
this->readpos = 0;
this->bufMaxSize = this->bufMaxSize + BUFFER_SIZE / 2;
// printf("buffMaxSize:%d, totalLen:%d ,list size:%d\n", (int)this->bufMaxSize, (int)this->totalLen, (int)lenList.size());
}
int lwsBuffer::read(void** data)
{
int ret = -1;
if (this->totalLen > 0)
{
int dataLen = lenList.front();
if((this->readpos + dataLen) <= this->bufMaxSize)
{
(*data) = (char *)this->buffer + this->readpos;
// printf("\n读取位置: %d ,读取信息 len: %d, data:%s\n", this->readpos, dataLen, (char*)data);
/* 修改 writepos 偏移 */
this->readpos += dataLen;
/* 计算数据长度*/
this->totalLen -= dataLen;
lenList.pop_front();
ret = dataLen;
}
else{
printf("从头开始读取\n");
this->readpos = 0;
(*data) = (char *)this->buffer + this->readpos;
// printf("\n读取位置: %d ,读取信息 len:%d, data:%s\n", this->readpos, dataLen, (char*)data);
this->readpos += dataLen;
/* 计算数据长度*/
this->totalLen -= dataLen;
lenList.pop_front();
ret = dataLen;
}
}
return ret;
}
int lwsBuffer::getTotalLen()
{
return this->totalLen;
}
模式二
写入读取数据,写入指定长度数据,和读取指定长度数据,模仿QBuffer的读写功能
环形缓冲区测试代码
#include "lwsBuffer.h"
#include<stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main()
{
char buf[1024],temp[1024];
int readFd = open("lwsBuffer.cpp",O_RDONLY);
int writeFd = open("temp.cpp",O_WRONLY|O_CREAT,0666);
int ret;
memset(buf, '\0', 1024);
memset(temp, '\0', 1024);
lwsBuffer Buff;
while (1)
{
/* code */
ret = read(readFd, buf, sizeof(buf));
if (ret == 0)
break;
int dataLen = Buff.write(buf,ret); //将数据保存到环形缓冲区
memset(buf, '\0', 1024);
printf("dataLen:%d\n",dataLen);
}
while(1)
{
memset(temp,'\0',sizeof(temp));
ret = Buff.read(temp, 1024); //从环形缓冲区中读取数据
if (ret == -1)
break;
/* code */
ret = write(writeFd, temp, ret);
printf("write ret:%d\n", ret);
}
close(readFd);
close(writeFd);
return 0;
}
makefile编译文件
test:test.cpp
g++ -o test test.cpp lwsBuffer.cpp
.PHONY:clean
clean:
rm -f test temp.cpp
运行结果
sh-4.3$ make
g++ -o test test.cpp lwsBuffer.cpp
sh-4.3$ ./test
dataLen:1024
dataLen:1024
dataLen:1024
dataLen:766
write ret:1024
write ret:1024
write ret:1024
write ret:766
执行后结果就是读取 lwsBuffer.cpp
里面的数据,储存到环形缓冲区,再从环形缓冲区读取数据写入到 temp.cpp
应用场景和优缺点
适用于少量数据的频繁读写
优点:多次memmove实现占空间最少的快速读写
缺点:当转发的数据很大且很频繁时,多次memmove会导致性能的减少
lwsBuffer.h
#pragma once
#ifndef _LWS_BUFFER_H_
#define _LWS_BUFFER_H_
#include <iostream>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define BUFFER_SIZE 1024
class lwsBuffer
{
private:
/* data */
void* buffer; /*数据*/
size_t bufMaxSize; /* 最大的存储数据大小 */
size_t bufLen; /* 存储的数据大小 */
int readpos,writepos; /* 读写位置 */
public:
lwsBuffer(/* args */);
~lwsBuffer();
public:
int write(void *data, int dataLen);
int read(void *data, int dataLen);
protected:
int writeAfter(void* data, int dataLen);
int writePre(void* data, int dataLen);
void _remalloc();
};
#endif
lwsBuffer.cpp
#include "lwsBuffer.h"
lwsBuffer::lwsBuffer(/* args */)
{
this->readpos = 0;
this->writepos = 0;
this->bufLen = 0;
this->bufMaxSize = BUFFER_SIZE;
this->buffer = malloc(this->bufMaxSize);
}
lwsBuffer::~lwsBuffer()
{
if (buffer)
free(buffer);
}
int lwsBuffer::write(void *data, int dataLen)
{
int ret = -1;
/* writePos 在 readPos 前 / readPos == writePos push*/
if (this->writepos >= this->readpos)
{
ret = writePre(data, dataLen);
}
/* readPos 在 writePos 前 push*/
else if(this->writepos < this->readpos)
{
ret = writeAfter(data, dataLen);
}
return ret;
}
int lwsBuffer::writePre(void* data, int dataLen)
{
int ret = -1;
/* 写入位置在前 */
/* buffer 后面有空间可以写入数据 预留一个字节不存储数据,用于区分*/
if ((this->writepos + dataLen) <= this->bufMaxSize)
{
//将数据写入到 buffer 里面去
memmove((char *)this->buffer + this->writepos, data, dataLen);
// printf("即将 push %d 消息 on:%d :%s\n", dataLen, prod->writepos, (char *)prod->buffer + prod->writepos + LWS_PRE);
this->writepos = this->writepos + dataLen;
ret = dataLen;
bufLen += dataLen;
}
else
{
/* buffer 后面没有空间可以写入数据 记录下*/
int temp = this->bufMaxSize - this->writepos;
temp = writePre(data, temp);
this->writepos = 0;
ret = writeAfter((char*)data + temp, dataLen - temp) + temp;
}
return ret;
}
int lwsBuffer::writeAfter(void *data, int dataLen)
{
int ret = -1;
/* 写入位置在后 */
/* writepos 到 readpos 有足够的空间可以写入数据 */
if ((this->writepos + dataLen) < this->readpos)
{
//将数据写入到 buffer 里面去
memmove((char *)this->buffer + this->writepos, data, dataLen);
this->writepos = this->writepos + dataLen;
ret = dataLen;
bufLen += dataLen;
}
else
{
this->_remalloc();
ret = writePre(data, dataLen);
}
return ret;
}
void lwsBuffer::_remalloc()
{
/* 数据满了 */
void *newBuf = malloc(this->bufMaxSize + BUFFER_SIZE / 2);
/* */
memmove(newBuf, (char*)this->buffer + this->readpos, this->bufMaxSize - this->readpos);
memmove((char*)newBuf + this->bufMaxSize - this->readpos, this->buffer, this->writepos);
free(this->buffer);
this->buffer = newBuf;
this->writepos = this->bufMaxSize - this->readpos + this->writepos;
this->readpos = 0;
this->bufMaxSize = this->bufMaxSize + BUFFER_SIZE / 2;
// printf("buffMaxSize:%d, bufLen:%d\n", (int)this->bufMaxSize,(int)this->bufLen);
}
int lwsBuffer::read(void *data, int dataLen)
{
int ret = -1;
if (this->bufLen > 0)
{
if (dataLen > this->bufLen)
{
dataLen = this->bufLen;
}
if ((this->readpos + dataLen) <= this->bufMaxSize)
{
memmove(data, (char *)this->buffer + this->readpos, dataLen);
// printf("\n读取位置: %d ,读取信息 len:%d,data:%s\n", this->readpos, dataLen, (char *)data);
this->readpos += dataLen;
this->bufLen -= dataLen;
ret = dataLen;
}
else
{
int temp = this->bufMaxSize - this->readpos;
temp = read(data, temp);
this->readpos = 0;
memmove((char*)data + temp, (char *)this->buffer, dataLen - temp);
// printf("\n读取位置: %d ,读取信息 len:%d, data:%s\n", this->readpos, dataLen, (char *)data);
this->readpos = this->readpos + dataLen - temp;
this->bufLen = this->bufLen - (dataLen - temp);
ret = dataLen;
}
}
return ret;
}