自动扩展分配的数据缓冲区

时间:2022-04-11 03:57:07

//  由于TCP是不分界限的字节流数据,一般在从socket 内核缓冲区读数据时根本不知道缓冲区里有多少数据。

//  可以通过recv 的MSG_PEEK 标致位窥看数据的长度, 再动态分配存储的缓冲区,但每次读取系统调用窥看和分配,

//  会影响程序的的性能.或者是在读和处理的速度不一致时,又不想多次拷贝,一次性没处理完的数据还继续保存在缓冲区里。

//  我们需要有一个自动扩展分配的缓冲来存储。

#define ALLOC_LEN                                (10 * 1024)                         //首次申请缓存大小, 默认为10K
#define MAX_ALLOC_LEN                     ((1024 * ALLOC_LEN))    //最大可申请的接收缓存(10 M)
#define REALLOC_BORDER_LEN       ((10 * 1024) / 80)               //重新申请的边界大小(首次分配的八十分之一), 注:边界大小可以自己定义

//数据信息缓冲
class CDataBuf
{
public:
    //构造函数
    CDataBuf() 
    {
        buf = (char*)malloc(ALLOC_LEN + 1);
        if(buf == NULL)
        {
            buf_len = 0;
            //LOG("Call realloc() error");
        }
        else
        {
            buf_len = ALLOC_LEN;
        }

        Clear();
    }

    //析构函数
    ~CDataBuf() 
    {
        if(NULL != buf)
        {
            free(buf);
            buf = NULL;
        }

        buf_len = 0;
        Clear();
    }

    //清空
    void Clear()
    {
        data_size   = 0;
        offset      = 0;
    }

    //重置,复位到原来的大小
    void Reset()
    {
        if(buf_len != ALLOC_LEN)
        {
            buf = (char*)realloc(buf, ALLOC_LEN);
            if(buf == NULL)
            {
                buf_len = 0;
                //LOG("Call realloc() error");
            }
            else
            {
                buf_len = ALLOC_LEN;
            }
        }

        Clear();
    }

    //功能: 1.检查缓冲区的数据是否达到再分配的边界值, 如果达到分配的边界值则以指数级再分配,并把原数据拷贝到新缓冲区中.
    //             2.检查缓冲区已分配的空间是否达到最大分配值,如果达到则返回false.
    //输入: 无
    //输出: 成功 true; 失败 false;

    bool CheckBuf()
    {
        if((buf_len - data_size) > REALLOC_BORDER_LEN)
        {
            //缓冲区的空闲长度还未达到再分配的边界
            return true;
        }

        //缓冲区的空闲长度已达到再分配的边界
        unsigned int realloc_len = buf_len << 1;

        do
        {
            if(realloc_len > MAX_ALLOC_LEN)
            {
                //增长后再分配的长度超过分配的最大值
                if(MAX_ALLOC_LEN > buf_len)
                {
                    //目前的长度还未达到最大长度,以最大长度分配
                    realloc_len = MAX_ALLOC_LEN;
                    break;
                }

                if(data_size >= buf_len)
                {
                    //缓冲区的数据长度已经达到缓冲区的最大长度, 返回失败
                    //LOG("data_size:%u, buf_len:%u\n", data_size, buf_len);
                    return false;
                }
           
                //缓冲区还有空间
                return true;
            }
        }while(0);

        buf = (char*)realloc(buf, realloc_len + 1);
        if(buf == NULL)
        {
            //分配失败
            buf_len = 0;
             Clear();
            //LOG("Call realloc() error");
            return false;
        }
        else
        {
            //重新分配成功
            buf_len = realloc_len;
        }

        return true;
    }

public:
    //缓冲区指针
    char*          buf;
    //缓冲区长度
    unsigned int   buf_len;
    //缓冲区里数据的长度

    unsigned int   data_size;
    //已处理的数据偏移长度
    unsigned int   offset;
};

//------------------ 举例缓冲区的处理 ------------------------

int readData(char* buf, const int read_len)
{
    int len = sprintf(buf, "%s", "abccccccccccccccc");
    return len;
}

int processData(const char* buf, const int data_len)
{
    printf(buf);


    return 0;
}

int main(int argc, char ** argv)
{
    CDataBuf read_buf;
    int read_bytes = 0;
    int proc_bytes = 0;

    do
    {
        if(!read_buf.CheckBuf())
        {
            //LOG("ERROR:Data buf not space.")
            //这里有可能是处理太慢,导致缓冲区满, 可以在这加个while processData(), 把数据处理完成再读
            break;
        }

        //读数据到缓冲区
        read_bytes = readData(read_buf.buf + read_buf.data_size, read_buf.buf_len - read_buf.data_size);
        if(read_bytes <= 0 )
        {
            //错误,或结束
            break;
        }

        //读到的数据累加到缓冲区已有的数据长度里

        read_buf.data_size += read_bytes;

        //处理缓冲区的数据
        proc_bytes = processData(read_buf.buf + read_buf.offset, read_buf.data_size - read_buf.offset);
        if(proc_bytes <= 0 )
        {
            //错误,或结束
            break;
        }

        if(proc_bytes < (read_buf.data_size - read_buf.offset))
        {
            //如果缓冲区的数据没有处理完
            read_buf.offset += proc_bytes;
        }
        else
        {
            //如果处理了缓冲区的所有数据,则清空重头开始存放
            read_buf.data_size = 0;
            read_buf.data_size = 0;
        }

    }while(true);

    //最后退出需要再次校验缓冲区里是否还有数据

    while((read_buf.data_size - read_buf.offset) > 0)
    {
        proc_bytes = processData(read_buf.buf + read_buf.data_size, read_buf.data_size - read_buf.offset);
        if(proc_bytes <= 0 )
        {
            //错误,或结束

            break;
        }

        read_buf.offset += proc_bytes;
    };
}