游戏后台里的通用技术之环形缓冲区

1.什么是环形缓冲区?
环形缓冲区一般是一段连续的buffer空间,空间可以是new或者使用vector<char>分配,这些空间对计算机来说是线性的,但我们在读取或者写入的时候需要%来计算读写的位置,
这样就抽象成环了。


2.什么时候用环形缓冲区
game的后台通常是几个程序来组成一个集群。这样几个后台进程必然需要通信。
比如下面一个简单的结构
 
 client--------- 接入层svr------logicsvr---------dbsvr


这样接入层在收到客户端数据包后需要把数据在发送给logicsvr,这样2个进程就需要通信 。进程间通信有多种方式,比如共享内存,socket方式等。
假如使用共享内存,那么2个进程如果读写而且还不用加锁(这里的读写是 一个只会去读数据,另一个只回去写数据)这个时候使用环形缓冲区就比较适合


下面是一个简单的实现 


#pragma once

#include <vector>
#define QUEUERESERVELENGTH 8   //保留8个字节不用
#define MAX_BUF_LEN  0x7FF0
class CCricleBuffer
{
public:
    CCricleBuffer(unsigned int MaxLen);
    ~CCricleBuffer(void);
    //缓存区是否已满
    bool IsFull();
    //写入数据
    bool AppendBuffer(char* pBuffer, short len);
    //获得一个完整的packet
    bool GetPacket(char* pBuffer, short& TranLen, short MaxLen);
private:
    //获得缓存区剩余大小
    unsigned int GetRemain();
private:
    std::vector<char>  m_CricleBuf;
    unsigned int   m_Len;
    unsigned int   m_ActiveLen;
    unsigned int   m_ReadPos;
    unsigned int   m_WritePos;
};




#include "StdAfx.h"
#include "CricleBuffer.h"

CCricleBuffer::CCricleBuffer(unsigned int MaxLen)
{

    m_Len = MaxLen;
    m_ActiveLen = MaxLen - QUEUERESERVELENGTH;
    m_ReadPos = 0;
    m_WritePos = 0;
    m_CricleBuf.resize(MaxLen);
}

CCricleBuffer::~CCricleBuffer(void)
{
}

unsigned int CCricleBuffer::GetRemain()
{
    unsigned int RemainLen = 0;
    if (m_ReadPos == m_WritePos)
    {
        RemainLen = m_Len;
    }
    else if (m_WritePos < m_ReadPos)
    {
        RemainLen = m_ReadPos - m_WritePos;
    }
    else
    {
        RemainLen = (m_Len - m_WritePos) + m_ReadPos;
    }
    
    return RemainLen;
}

bool CCricleBuffer::IsFull()
{
    unsigned int RemainLen = GetRemain();
    if (RemainLen - QUEUERESERVELENGTH > 0)
    {
        return false;
    }
    return true;
}

bool CCricleBuffer::AppendBuffer(char* pBuffer, short len)
{
    if (pBuffer == NULL || len == 0)
    {
        //print log
        return false;
    }
    
    if (m_ReadPos < 0 || m_ReadPos > m_Len ||
        m_WritePos <0 || m_WritePos > m_Len)
    {
        //print log
        return false;
    }
    
    if (IsFull())
    {
        //print log
        return false;
    }
    unsigned int RemainLen = GetRemain() - QUEUERESERVELENGTH;
    if (len + sizeof(len) > RemainLen)
    {
        //print log
        return false;
    }

    char* pDest = (char*)&len;
    int shortlen = sizeof(len);
    for (int i = 0; i < shortlen; ++i)
    {
        m_CricleBuf[m_WritePos] = pDest[i];
        m_WritePos = (m_WritePos + 1) % m_Len;
    }

    if (m_WritePos < m_ReadPos)
    {
        memcpy((void*)&m_CricleBuf[m_WritePos],pBuffer,len);
    }
    else
    {
        if (m_Len - m_WritePos > len)
        {
            memcpy((void*)&m_CricleBuf[m_WritePos],(const void*)pBuffer,len);
        }
        else
        {
            //需要做2次copy
            unsigned int half = m_Len - m_WritePos;
            memcpy((void*)&m_CricleBuf[m_WritePos],(const void*)pBuffer,half);
            memcpy((void*)&m_CricleBuf[0],(const void*)pBuffer[half], len - half);
        }
    }
    m_WritePos = (m_WritePos + m_Len) % m_Len;  
    return true;
}

//pBuffer 输出缓存区瘦地址
//TranLen 实际的读取buffer长度
bool CCricleBuffer::GetPacket(char* pBuffer, short& TranLen, short MaxLen)
{
    if (NULL== pBuffer || MaxLen > MAX_BUF_LEN)
    {
        //print log
        return false;
    }

    if (m_ReadPos < 0 || m_ReadPos > m_Len ||
        m_WritePos <0 || m_WritePos > m_Len)
    {
        //print log
        return false;
    }

    if (IsFull())
    {
        //print log
        return false;
    }

    unsigned int RemainLen = GetRemain() - QUEUERESERVELENGTH;
    if (RemainLen <= sizeof(short))
    {
        //print log
        return false;
    }

    //读short长度,为后面一个完整的packet的长度
    unsigned int shortlen = sizeof(short);
    char* buff = (char*)&TranLen;
    for (int i = 0;i < shortlen; ++i)
    {
        buff[i] = m_CricleBuf[m_ReadPos];
        m_ReadPos = (m_ReadPos + 1) % m_Len;
    }
    //判断后面的长度是否有一个完整的packet    
    if (RemainLen  - sizeof(short) <  TranLen || TranLen <=0)
    {
        //print log
        return false;
    }

    if (m_ReadPos < m_WritePos)
    {
        memcpy((void*)pBuffer,(const void*)&m_CricleBuf[m_ReadPos],TranLen);
    }
    else
    {
        unsigned int half = m_Len - m_ReadPos;
        if (half > TranLen)
        {
            memcpy((void*)pBuffer,(const void*)&m_CricleBuf[m_ReadPos],TranLen);
        }
        else
        {
            memcpy((void*)pBuffer,(const void*)&m_CricleBuf[m_ReadPos],half);
            memcpy((void*)pBuffer[half],(const void*)&m_CricleBuf[0],TranLen-half);
        }
    }
    m_ReadPos = (m_ReadPos + TranLen) % m_Len;
    return true;
}









                
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值