服務器框架——網絡消息協議

一、介紹

用於服務器和客戶端應用層之間的協議,通過定義的協議,可以將接收的數據轉發給相應的模塊,相應模塊可以將數據按照約定的結構進行解析。

 

二、協議體

2.1 消息碼定義

消息碼分爲:分類消息碼,主消息碼,子消息碼

2.2 消息碼作用

消息碼用於消息轉發,將消息一層一層傳給對應模塊。消息碼作用介紹:

  1. 分類消息碼,用於網關服轉發消息,網關服通過分類消息碼,將消息發送給對應的服務器
  2. 主消息碼,將消息轉發給對應模塊
  3. 子消息碼,一個模塊中定義的消息碼,用於處理一個具體某個邏輯

在這三種模塊中,我們還可以根據實際業務需要添加新的消息碼類型。例如,房間插件中的牌桌消息,牌桌消息是與牌桌遊戲相關的一類消息,它可以根據不同遊戲定義各自的消息類型,爲了方便擴展,我們可以將這類消息共用同一個【子消息碼】,然後在【子消息碼】後面再加一個【遊戲消息碼】,這樣不同遊戲可以各自定義自己的遊戲消息,遊戲消息碼爲:分類消息碼(遊戲服),主消息碼(房間插件),子消息碼(房間數據),某個遊戲消息碼(具體遊戲消息碼),數據。

說明:廣場服和遊戲服,在收到網關服消息,除了登錄消息外所有消息都是發送到對應插件中,插件自己處理各種的消息。每個插件都會定義一個自己的消息碼,網關服代理通過主消息碼將消息發送給有關的插件。

 

2.3 消息結構

以遊戲中瀏覽郵件爲例

1.分類消息和主消息定義

//服務器分類消息
enum CMD_ROOT
{
	CMDROOT_GATEWAY_MSG = 0x0,		//代理前端消息
	CMDROOT_LOGIN_MSG = 0x1,		//登錄服務器消息
	CMDROOT_PLAZA_MSG = 0x2,		//廣場消息
	CMDROOT_GAMESERVER_MSG = 0x3,	//遊戲服務器消息
	CMDROOT_WEB_MSG = 0x4,			//WEB服務器消息
	CMDROOT_MAX
};

//遊戲主消息碼定義
enum GS_PLAZA_MSGID
{
	// 前面的消息碼省略
	GS_PLAZA_MSGID_EMAIL = 11,	//	郵件		c->s->c
	// 後面的消息碼省略

	GS_PLAZA_MSGID_MAX
};

2. 消息頭定義

typedef  unsigned char uchar;
template<uchar rootid>
struct GS_Head
{
	uchar RootID;		//分類消息碼
	uchar MainID;		//主消息碼
	uchar SubID;		//子消息碼

	GS_Head() :RootID(rootid){}
};

3. 廣場服消息頭定義

//廣場消息頭
struct GS_PlazaHead : public GS_Head < CMDROOT_PLAZA_MSG >
{
	inline void InitRoot(uchar mainid, uchar subid)
	{
        RootID = CMDROOT_PLAZA_MSG;
        MainID = mainid;
        SubID = subid;
	}
};

4. 郵件消息碼定

// 郵件消息定義
enum GS_PLAZA_EMAILL_MSG
{
	PLAZA_EMAIL_VIEW = 0,	 //  瀏覽郵件 c->s
	// 後面消息省略

	PLAZA_EMAIL_MAX
};

5. 郵件消息協議定義

template<uchar subid>
struct GS_EmailHead : public GS_PlazaHead
{
	GS_EmailHead() { InitRoot(); }
	inline void InitRoot()
	{
        GS_PlazaHead::InitRoot(GS_PLAZA_MSGID_EMAIL, subid);
	}
};

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//PLAZA_EMAIL_VIEW 客戶端請求瀏覽郵件
struct GS_EmailView : public GS_EmailHead<PLAZA_EMAIL_VIEW>
{
	GS_EmailView()
	{
        InitRoot();
	}
};

6. 在發送變長數據的時候,一個字段用於記錄變長數據大小,數據可以用一個長度爲0的數組記錄,內存需要自己申請。

struct GS_XXX : public GS_XXXHead<PLAZA_XXX>
{
	int len;
	uchar data[0];
	GS_XXX()
	{
         memset(this, 0, sizeof(*this));
         InitRoot();
	}
};

 

三、協議組裝

爲了方便組裝發送的數據或解析接收的數據,我們可以對發送數據組裝功能進行一層封裝。這裏提供三種方法

3.1 簡單存檔接口

存檔接口

// 存檔接口
struct IArchive
{
	//清理
        virtual void			Clear() = 0;
	//釋放
	virtual void			Release() = 0;

	// 請求寫入緩衝。nLen < 4K
	// 這裏有個假定:對象必須按順序寫入自身的數據,因爲CMapContainer保存對象時
	virtual bool			Write(const void* buf,int nLen) = 0;
	
	// 請求讀出指定數量的數據
	virtual bool			Read(void* buf,int nLen) = 0;
	
	// 獲取當前緩衝指針所指的數據地址,適合直接操作緩衝區
	virtual void*			GetBuffer() = 0;

	//獲得頭指針的數據地址
	virtual void*			GetHead() = 0;
	
	//獲得某個位置的數據地址
	virtual void*			GetPos(int nPos) = 0;
	
	// 把當前指針向後移動nOffset字節,返回當前指針
	// 如果操作失敗,Seek返回-1
	virtual sint			Seek(sint nOffset = 0) = 0;
	
	// 將指針移動指定位置
	virtual sint			SeekTo(sint nPtr = 0) = 0;
	
	// 獲得當前緩衝指針偏移
	virtual sint			GetBufferOffset() = 0;
	
	// 讀取數據時,獲取剩餘數據長度,寫數據時,返回有效緩衝區長度
	virtual sint			GetLeftBufferLen() = 0;

	//獲得緩衝區的長度
	virtual sint			GetBufferLen() = 0;

	// 寫入一個字符串
	virtual bool			WriteString(const stchar* pCh)=0;
	// 寫入一個BYTE
	virtual bool			WriteBYTE(const uchar nValue) = 0;
	// 寫入一個WORD
	virtual bool			WriteWORD(const ushort nValue) = 0;
	// 寫入一個DWORD
	virtual bool			WriteDWORD(const ulong dwValue) = 0;
	// 寫入一個slong
	virtual bool			WriteLONG(const slong lValue) = 0;
	// 寫入一個sint64
	virtual bool			WriteINT64(const sint64 lValue64) = 0;
	//讀取一個字符串
	virtual stchar*			ReadString(stchar* szBuffer, ushort& nMaxLen) = 0;
	//讀取一個BYTE
	virtual uchar			ReadBYTE() = 0;
	//讀取一個WORD
	virtual ushort			ReadWORD() = 0;
	//讀取一個DWORD
	virtual ulong			ReadDWORD() = 0;
        //讀取一個slong
        virtual slong			ReadLONG() = 0;
        //讀取一個sint64
        virtual sint64			ReadINT64() = 0;
};

//自動釋放存檔接口
struct AutoRleaseArchive
{
    AutoRleaseArchive(IArchive* pArchive) :m_pArchive(pArchive){}
    ~AutoRleaseArchive(){ m_pArchive->Release(); }
    IArchive* operator->() { return m_pArchive; }
    IArchive*	m_pArchive;
};

 

#pragma once
#include "IArchive.h"

//////////////////////////////////////////////////////////////////////////
//框架使用的存檔接口
class CDataArchive : public IArchive
{
protected:
	// 構造函數, 僅用於被繼承
	CDataArchive(void);

	// 初始化數據
	void Init(void* szBindBuffer, sint nMaxSize);

public:
	CDataArchive(sint nMaxSize);
	~CDataArchive();

public:
	//銷燬
	void Destory();
	//重啓用
	void ResetUse();
	//判斷是否可以重用
	bool CanResetUse(sint& uLen);
	//判斷能否銷燬
	bool CanDestory();
	//檢測
	bool Check();

	//IArchive
public:
	// 寫入一個BYTE
	bool WriteBYTE(const uchar nValue)final;

	// 寫入一個WORD
	bool WriteWORD(const ushort nValue)final;

	// 寫入一個DWORD
	bool WriteDWORD(const ulong dwValue)final;

	// 寫入一個slong
	bool WriteLONG(const slong lValue)final;

	// 寫入一個sint64
	bool WriteINT64(const sint64 lValue64)final;

	// 寫入一個字符串
	bool WriteString(const stchar* pCh)final;

	// 請求寫入數據
	bool Write(const void *buf, sint nLen)final;

public:	
	// 請求讀出批定數量的數據
	bool Read(void *buf, sint nLen)final;

	//讀取一個字符串
	stchar* ReadString(stchar* szBuffer, ushort& nMaxLen)final;

	//讀取一個BYTE
	uchar	ReadBYTE()final;

	//讀取一個WORD
	ushort	ReadWORD()final;

	//讀取一個DWORD
	ulong	ReadDWORD()final;

	//讀取一個sint64
	sint64 ReadINT64()final;

	//讀取一個LONG
	slong ReadLONG()final;

	// 獲取當前緩衝指針所指的數據地址,適合直接操作緩衝區
	void* GetBuffer()final;

	//獲得頭指針的數據地址
	void* GetHead()final;

	//獲得某個位置的數據地址
	void* GetPos(sint nPos)final;

	// 把當前指針向後移動nOffset字節,返回移動前的指針
	// 設置nOffSet即可獲得當前緩衝指針偏移
	// 如果操作失敗,Seek返回-1
	sint Seek(sint nOffset)final;

	// 同上,把緩衝指針移動到指定位置,返回移動前的指針
	sint SeekTo(sint nPtr = 0)final;

	// 獲得當前緩衝指針偏移
	sint GetBufferOffset(void)final;

	// 讀取數據時,獲取剩餘數據長度,寫數據時,返回有效緩衝區長度
	sint GetLeftBufferLen(void)final;

	//獲得緩衝區的長度
	sint GetBufferLen()final;

	//清理
	void Clear()final;

	//釋放
	void Release()final;

private:
	sint				m_nOffset;
	uchar*				m_pBindBuffer;
	sint				m_nMaxSize;
	uchar*				m_pNewBuffer;
	ushort				m_referenceCount;
};
#include "Archive.h"

// 構造函數, 僅用於被繼承
CDataArchive::CDataArchive(void) :m_referenceCount(1),m_nOffset(0),m_pBindBuffer(0),m_nMaxSize(0){}

// 初始化數據
void CDataArchive::Init(void* szBindBuffer, sint nMaxSize)
{
    if (0 == szBindBuffer || nMaxSize <= 0)
        return;

    m_nOffset = 0;
    m_pBindBuffer = (uchar*)szBindBuffer;
    m_nMaxSize = nMaxSize;
}

CDataArchive::CDataArchive(sint nMaxSize) :m_referenceCount(1)
{
#ifdef MEONRYCHECK
    m_pNewBuffer = (uchar*)malloc(nMaxSize + sizeof(uchar));	
    ((uchar*)m_pNewBuffer)[nMaxSize] = 0xED;
#else
    m_pNewBuffer = (uchar*)malloc(nMaxSize);
#endif

    m_nOffset = 0;
    m_pBindBuffer = 0;
    m_nMaxSize = 0;
    Init(m_pNewBuffer, nMaxSize);
}

CDataArchive::~CDataArchive()
{
    if (m_pNewBuffer != NULL && Check())
    {
        free(m_pNewBuffer);
        m_pNewBuffer = NULL;
    }
}

//銷燬
void CDataArchive::Destory()
{
    delete this;
}

//重啓用
void CDataArchive::ResetUse()
{
    if (m_referenceCount != 0)
        g_pGlobalServer->GetTrace()->TraceErrorLn(TEXT("CDataArchive::ResetUse:%d"), m_referenceCount);
    else
    {
        m_nOffset = 0;
        ++m_referenceCount;
    }
}

//判斷是否可以重用
bool CDataArchive::CanResetUse(sint& uLen)
{
    return m_referenceCount == 0 && m_nMaxSize == uLen;
}

//判斷能否銷燬
bool CDataArchive::CanDestory()
{
    return m_referenceCount == 0;
}

//檢測
bool CDataArchive::Check()
{
#ifdef MEONRYCHECK
    if (m_pNewBuffer != NULL && ((uchar*)m_pNewBuffer)[m_nMaxSize] != 0xED)
    {
        if (m_nMaxSize >= sizeof(GS_HeadNull))
        {
            GS_HeadNull* pHead = (GS_HeadNull*)m_pNewBuffer;
            g_pGlobalServer->GetTrace()->TraceErrorLn(TEXT("數據打包器內存泄露:%d,%d,%d"), pHead->RootID, pHead->MainID, pHead->SubID);
        }
        else
            g_pGlobalServer->GetTrace()->TraceErrorLn(TEXT("數據打包器內存泄露"));

        return false;
    }
#endif

    return true;
}

// 寫入一個BYTE
bool CDataArchive::WriteBYTE(const uchar nValue)
{
    return Write(&nValue, sizeof(const uchar));
}

// 寫入一個WORD
bool CDataArchive::WriteWORD(const ushort nValue)
{
    return Write(&nValue, sizeof(const ushort));
}

// 寫入一個DWORD
bool CDataArchive::WriteDWORD(const ulong dwValue)
{
    return Write(&dwValue, sizeof(const ulong));
}

// 寫入一個slong
bool CDataArchive::WriteLONG(const slong lValue)
{
    return Write(&lValue, sizeof(slong));
}

// 寫入一個sint64
bool CDataArchive::WriteINT64(const sint64 lValue64)
{
    return Write(&lValue64, sizeof(sint64));
}

// 寫入一個字符串
bool CDataArchive::WriteString(const stchar* pCh)
{
    if (0 == pCh)
        return false;

    ushort wLen = (ushort)(_tcslen(pCh));
    if (!Write(&wLen, sizeof(wLen)))
        return false;

    if (!Write(pCh, wLen*sizeof(stchar)))
        return false;

    return true;
}

// 請求寫入數據
bool CDataArchive::Write(const void *buf, sint nLen)
{
    if (m_nOffset + nLen > m_nMaxSize)
        return false;

     if (0 == buf)
         return false;

    if (0 >= nLen)
        return false;

    memcpy(m_pBindBuffer + m_nOffset, buf, nLen);
    m_nOffset += nLen;
    return true;
}

// 請求讀出批定數量的數據
bool CDataArchive::Read(void *buf, sint nLen)
{
    if (0 == buf || 0 >= nLen || m_nOffset + nLen > m_nMaxSize)
        return false;

    memcpy(buf, m_pBindBuffer + m_nOffset, nLen);
    m_nOffset += nLen;
    return true;
}

//讀取一個字符串
stchar* CDataArchive::ReadString(stchar* szBuffer, ushort& nMaxLen)
{
    ushort wLen;
    if (!Read(&wLen, sizeof(wLen)))
        return NULL;

    if (wLen >= nMaxLen)
        return NULL;

    if (!Read(szBuffer, wLen*sizeof(stchar)))
        return NULL;

    nMaxLen = wLen;
    return szBuffer;
}

//讀取一個BYTE
uchar CDataArchive::ReadBYTE()
{
    uchar nValue = 0;
    Read(&nValue, sizeof(nValue));
    return nValue;
}

//讀取一個WORD
ushort CDataArchive::ReadWORD()
{
    ushort nValue = 0;
    Read(&nValue, sizeof(nValue));
    return nValue;
}

//讀取一個DWORD
ulong CDataArchive::ReadDWORD()
{
    ulong nValue = 0;
    Read(&nValue, sizeof(nValue));
    return nValue;
}

//讀取一個sint64
sint64 CDataArchive::ReadINT64()
{
    sint64 nValue = 0;
    Read(&nValue, sizeof(nValue));
    return nValue;
}

//讀取一個LONG
slong CDataArchive::ReadLONG()
{
    slong nValue = 0;
    Read(&nValue, sizeof(nValue));
    return nValue;
}

// 獲取當前緩衝指針所指的數據地址,適合直接操作緩衝區
void* CDataArchive::GetBuffer()
{
    return m_pBindBuffer + m_nOffset;
}

//獲得頭指針的數據地址
void* CDataArchive::GetHead()
{
    return m_pBindBuffer;
}

//獲得某個位置的數據地址
void* CDataArchive::GetPos(sint nPos)
{
    if (nPos < 0 || nPos >= m_nMaxSize)
        return NULL;

    return &m_pBindBuffer[nPos];
}

// 把當前指針向後移動nOffset字節,返回移動前的指針
// 設置nOffSet即可獲得當前緩衝指針偏移
// 如果操作失敗,Seek返回-1
sint CDataArchive::Seek(sint nOffset)
{
    if (m_nOffset + nOffset > m_nMaxSize || m_nOffset + nOffset < 0)
        return -1;

    sint nOld = m_nOffset;
    m_nOffset += nOffset;
    return nOld;
}

// 同上,把緩衝指針移動到指定位置,返回移動前的指針
sint CDataArchive::SeekTo(sint nPtr)
{
    if (nPtr > m_nMaxSize || nPtr < 0)
        return -1;

    sint nOld = m_nOffset;
    m_nOffset = nPtr;
    return nOld;
}

// 獲得當前緩衝指針偏移
sint CDataArchive::GetBufferOffset(void)
{
    return m_nOffset;
}

// 讀取數據時,獲取剩餘數據長度,寫數據時,返回有效緩衝區長度
sint CDataArchive::GetLeftBufferLen(void)
{
    return m_nMaxSize - m_nOffset;
}

//獲得緩衝區的長度
sint CDataArchive::GetBufferLen()
{
    return m_nMaxSize;
}

//清理
void CDataArchive::Clear()
{
    SeekTo(0);
}

//釋放
void CDataArchive::Release()
{
    Check();
    if (m_referenceCount == 0)
        g_pGlobalServer->GetTrace()->TraceErrorLn(TEXT("CDataArchive::Release:%d"), m_referenceCount);
    else
        --m_referenceCount;
}

存檔接口管理

//存檔接口管理
class CArchiveMgr
{
public:
    //獲得單例對象
    static CArchiveMgr* GetInstance();
	
    //獲得一個存檔接口對象
    IArchive* NewArchive(sint nMaxSize);

    //釋放存檔接口
    void Close();
    
    //銷燬對象
    void ReleaseArchive(CDataArchive* pArchive);

private:
    typedef std::vector<CDataArchive*>            ARCHIVEVEC;
    typedef std::unordered_map<sint, ARCHIVEVEC>  ARCHIVEMAP;

    ARCHIVEMAP			m_ArchiveMap;	//緩存的存檔接口
};
//////////////////////////////////////////////////////////////////////////
//獲得單例對象
CArchiveMgr* CArchiveMgr::GetInstance()
{
    static CArchiveMgr archivemgr;
    return &archivemgr;
}

//獲得一個存檔接口對象
IArchive* CArchiveMgr::NewArchive(sint nMaxSize)
{
    ARCHIVEVEC& archivevec = m_ArchiveMap[nMaxSize];
    for (ARCHIVEVEC::iterator itr = archivevec.begin(); itr != archivevec.end(); ++itr)
    {
        if ((*itr)->CanResetUse(nMaxSize))
        {
            (*itr)->ResetUse();
            return *itr;
        }
    }

    CDataArchive* pArchive = new CDataArchive(nMaxSize);
    archivevec.push_back(pArchive);
    return pArchive;
}

//釋放存檔接口
void CArchiveMgr::Close()
{	
    for (ARCHIVEMAP::iterator mapitr = m_ArchiveMap.begin(); mapitr != m_ArchiveMap.end(); ++mapitr)
    {
        ARCHIVEVEC& archivevec = mapitr->second;
        for (ARCHIVEVEC::iterator vecitr = archivevec.begin(); vecitr != archivevec.end(); ++vecitr)
        {
            ReleaseArchive(*vecitr);			
        }
        archivevec.clear();
    }

    m_ArchiveMap.clear();
}

//銷燬對象
void CArchiveMgr::ReleaseArchive(CDataArchive* pArchive)
{
    GS_HeadNull head;
    if (pArchive->GetBufferLen() >= sizeof(GS_HeadNull))
        head = *((GS_HeadNull*)pArchive->GetHead());

    pArchive->Destory();
}

 

3.2 流結構的讀寫,支持自動擴展

#pragma once

#include "IArchive.h"
#include <list>
#include <set>

class IStreamBuffer;

struct IStreamObject
{
    virtual void serial(IStreamBuffer& buf) = 0;
    virtual void unserial(IStreamBuffer& buf) = 0;
};

class IStreamBuffer
{
public:
    IStreamBuffer() : m_pBuff(NULL)	, m_posRead(0)	, m_posWrite(0)	, m_validateSize(0), m_bBad(false) {};

    virtual ~IStreamBuffer() { _Clear(); }
    virtual void Clear() { _Clear(); }

    IStreamBuffer& operator=(const IStreamBuffer& r)
    {
        if (this != &r)
        {
            Clear();	
            Write(r.m_pBuff, r.m_validateSize);
            m_posRead = r.m_posRead;
            m_bBad = r.m_bBad;
        }

        return *this;
    }

    char*	Data() const { return m_pBuff; }
    char*	GetReadBuffer() const { return m_pBuff + m_posRead; }
    char*	GetWriteBuffer() const { return m_pBuff + m_posWrite; }

    ulong	GetRemainSize() const { return m_validateSize - m_posRead; }
    ulong	Size() const { return m_validateSize; }
    ulong	BufferSize() const { return m_buffSize; }
    ulong	PosRead() const	{ return m_posRead; }
    ulong	PosWrite() const { return m_posWrite; }

    bool    SeekToWrite(ulong posWrite) 
    { 
        Resize(posWrite);
        if (posWrite < m_buffSize)
        {
            m_posWrite = posWrite;
            m_validateSize = posWrite;
            return true;
        }

        return false;
    }

    void	SkipRead(ulong size) 
    { 
        if (m_posRead + size <= m_validateSize) m_posRead += size; 
        else m_bBad = true;
    }
    void    SkipWrite(ulong size) { Resize(m_posWrite + size); m_posWrite += size; m_validateSize += size; }

    virtual void Write(void* src, ulong size)
    {
        ulong newSize = m_posWrite + size;
        Resize(newSize);

        if (newSize <= m_buffSize)
        {
            memcpy(&m_pBuff[m_posWrite], src, size);
            m_posWrite += size;
            m_validateSize += size;
        }
        else
        {
            m_bBad = true;
        }
    }

    virtual void Read(void* dest, ulong size)
    {
        if (m_posRead + size <= m_validateSize)
        {
            memcpy(dest, &m_pBuff[m_posRead], size);
            m_posRead += size;
        }
        else
        {
            m_bBad = true;
        }
    }

    virtual void Put(ulong pos, void* src, ulong size)
    {
        ulong newSize = pos + size;
        Resize(newSize);
        if (newSize <= m_buffSize)
        {
            memcpy(&m_pBuff[pos], src, size);
        }
        else
        {
            m_bBad = true;
        }
    }

    template<typename T> void _Write(const T& value) { Write((void*)&value, sizeof(T)); }
    template<typename T> void _Read(T& value) { Read((void*)&value, sizeof(T)); }
    template<typename T> void _Put(ulong pos, const T& value) { Put(pos, (void*)&value, sizeof(T)); }

    IStreamBuffer& operator<<(const std::wstring& wstr)
    {
        ushort size = (ushort)wstr.size();
        _Write(size);
        if (size > 0)
        {
            Write((void*)wstr.data(), size*sizeof(wchar_t));
        }
        return *this;
    }

    IStreamBuffer& operator>>(std::wstring& wstr)
    {
        ushort size = 0;
        _Read(size);
        wstr.clear();
        ulong byteCount = size * sizeof(wchar_t);
        if (size > 0 && m_posRead + byteCount <= m_validateSize)
        {
            wstr.append((const wchar_t*)(&m_pBuff[m_posRead]), size);
            SkipRead(byteCount);
        }

        return *this;
    }

    IStreamBuffer& operator<<(const std::string& str)
    {
        ushort size = (ushort)str.size();
        _Write(size);
        if (size > 0)
        {
            Write((void*)str.data(), size);
        }
        return *this;
    }

    IStreamBuffer& operator>>(std::string& str)
    {
        ushort size = 0;
        _Read(size);
        str.clear();
        if (size > 0 && m_posRead + size <= m_validateSize)
        {
            str.append((const char*)m_pBuff[m_posRead], size);
            SkipRead(size);
        }

        return *this;
    }

    IStreamBuffer& operator<<(const char* str)
    {
        this->operator<<(std::string(str));
    }

    IStreamBuffer& operator<<(const wchar_t* str)
    {
        this->operator<<(std::wstring(str));
    }

    IStreamBuffer& operator<<(const char& c) { _Write<char>(c); return *this; }
    IStreamBuffer& operator>>(char& c) { _Read<char>(c); return *this; }

    IStreamBuffer& operator<<(const bool& c) { _Write<bool>(c); return *this; }
    IStreamBuffer& operator>>(bool& c) { _Read<bool>(c); return *this; }

    IStreamBuffer& operator<<(const sint8& c) { _Write<sint8>(c); return *this; }
    IStreamBuffer& operator>>(sint8& c) { _Read<sint8>(c); return *this; }

    IStreamBuffer& operator<<(const uint8& c) { _Write<uint8>(c); return *this; }
    IStreamBuffer& operator>>(uint8& c) { _Read<uint8>(c); return *this; }

    IStreamBuffer& operator<<(const sint16& c) { _Write<sint16>(c); return *this; }
    IStreamBuffer& operator>>(sint16& c) { _Read<sint16>(c); return *this; }

    IStreamBuffer& operator<<(const uint16& c) { _Write<uint16>(c); return *this; }
    IStreamBuffer& operator>>(uint16& c) { _Read<uint16>(c); return *this; }

    IStreamBuffer& operator<<(const sint32& c) { _Write<sint32>(c); return *this; }
    IStreamBuffer& operator>>(sint32& c) { _Read<sint32>(c); return *this; }

    IStreamBuffer& operator<<(const uint32& c) { _Write<uint32>(c); return *this; }
    IStreamBuffer& operator>>(uint32& c) { _Read<uint32>(c); return *this; }

    IStreamBuffer& operator<<(const slong& c) { _Write<slong>(c); return *this; }
    IStreamBuffer& operator>>(slong& c) { _Read<slong>(c); return *this; }

    IStreamBuffer& operator<<(const ulong& c) { _Write<ulong>(c); return *this; }
    IStreamBuffer& operator>>(ulong& c) { _Read<ulong>(c); return *this; }

    IStreamBuffer& operator<<(const sint64& c) { _Write<sint64>(c); return *this; }
    IStreamBuffer& operator>>(sint64& c) { _Read<sint64>(c); return *this; }

    IStreamBuffer& operator<<(const uint64& c) { _Write<uint64>(c); return *this; }
    IStreamBuffer& operator>>(uint64& c) { _Read<uint64>(c); return *this; }

    IStreamBuffer& operator<<(const float& c) { _Write<float>(c); return *this; }
    IStreamBuffer& operator>>(float& c) { _Read<float>(c); return *this; }

    IStreamBuffer& operator<<(IStreamObject& base) { base.serial(*this); return *this; }
    IStreamBuffer& operator>>(IStreamObject& base){ base.unserial(*this); return *this; }

    template<typename T>
    IStreamBuffer& operator<<(std::vector<T>& vec)
    {
        ulong count = (ulong)vec.size();
        Write(&count, sizeof(count));

        if (count > 0)
        {
            for (auto& v : vec) *this << v;
        }
        return *this;
    }

    template<typename T>
    IStreamBuffer& operator>>(std::vector<T>& vec)
    {
        vec.clear();
        ulong count = 0;
        Read(&count, sizeof(count));

        if (count > 0)
        {
            vec.resize(count);
            for (ulong i = 0; i < count; ++i) *this >> vec[i];
        }
        return *this;
    }

    template<typename T>
    IStreamBuffer& operator<<(std::list<T>& list)
    {
        ulong count = (ulong)list.size();
        Write(&count, sizeof(count));

        if (count > 0)
        {
            for (auto& v : list)
            {
                *this << v;
            }
        }

        return *this;
    }

    template<typename T>
    IStreamBuffer& operator>>(std::list<T>& list)
    {
        list.clear();
        ulong count = 0;
        Read(&count, sizeof(count));

        if (count > 0)
        {
            T temp;
            for (ulong i = 0; i < count; ++i)
            {
                *this >> temp;
                list.push_back(temp);
            }
        }

        return *this;
    }

    template<typename T>
    IStreamBuffer& operator<<(std::set<T>& set)
    {
        ulong count = (ulong)set.size();
        Write(&count, sizeof(count));

        if (count > 0)
        {
            for (auto& v : set) *this << v;
        }

        return *this;
    }

    template<typename T>
    IStreamBuffer& operator>>(std::set<T>& set)
    {
        set.clear();
        ulong count = 0;
        Read(&count, sizeof(count));

        if (count > 0)
        {
            T temp;
            for (ulong i = 0; i < count; ++i)
            {
                *this >> temp; 
                set.insert(temp);
            }
        }

        return *this;
    }

    IStreamBuffer& operator<<(const IStreamBuffer& c)
    {
        ulong size = c.Size();
        _Write(size);
        Write(c.m_pBuff, size);
        return *this;
    }

    IStreamBuffer& operator>>(IStreamBuffer& c)
    {
        ulong size = 0;
        _Read(size);
        if (m_posRead + size <= m_validateSize)
        {
            c.Write(&m_pBuff[m_posRead], size);
        }
        return *this;
    }

    bool IsBad() { return m_bBad; }
    bool IsOk() { return !m_bBad; }

    bool Resize(ulong newSize)
    {
        if (newSize > m_buffSize) { _Resize(newSize); }

        return m_buffSize >= newSize;
    }

protected:
    void _Clear()
    {
        m_buffSize = 0;
        m_posRead = 0;
        m_posWrite = 0;
        m_validateSize = 0;
        m_bBad = false;
        m_pBuff = NULL;
    }

    virtual void _Resize(ulong newSize) = 0;

protected:
    ulong	m_buffSize;     // 緩存大小
    ulong	m_posRead;      // 讀pos
    ulong	m_posWrite;     // 寫pos
    ulong	m_validateSize; // 有效數據大小
    bool    m_bBad;         // 狀態

    char*	m_pBuff;        // 緩存
};

template<ulong defSize>
class CBuffer : public IStreamBuffer
{
public:
    CBuffer() 
    {
        Init();
    };

    virtual ~CBuffer() { _Clear(); }

    virtual void Clear()	// reset
    {
        _Clear();
        IStreamBuffer::Clear();
        Init();
    }

    CBuffer(const CBuffer& r)
    {
        Clear();
        IStreamBuffer::operator=(r);
    }

    CBuffer& operator=(const CBuffer& r)
    {
        if (this != &r)
        {
            Clear();
            IStreamBuffer::operator=(r);
        }
        
        return *this;
	}

    void Init() 
    {
        m_buffSize = defSize;
        m_pBuff = &m_buffInternal[0];
    }

protected:
    void _Clear()
    {
        if (m_pBuff && m_pBuff != &m_buffInternal[0])
        {
            delete[] m_pBuff;、
            m_pBuff = NULL;
            m_buffSize = 0;
        }
    }

    virtual void _Resize(ulong newSize)
    {
        if (newSize > m_buffSize)
        {
            newSize = (newSize > m_buffSize * 2) ? newSize : (m_buffSize * 2);
            char* newBuf = new char[newSize];

            if (m_pBuff && Size() > 0)
            {
                memcpy(newBuf, m_pBuff, Size());
            }

            _Clear();

            m_pBuff = newBuf;
            m_buffSize = newSize;
        }
    }

private:
    char m_buffInternal[defSize];
};

class CReadBufferHelper : public IStreamBuffer
{
public:
    CReadBufferHelper(void* pData, ulong size)
    {
        m_pBuff = (char*)pData;
        m_buffSize = size;
        m_validateSize = size;
    }

    ~CReadBufferHelper() {};

    virtual void _Resize(ulong newSize) { };
    virtual void Write(void* src, ulong size) { /*throw "read only, disable write feature.";*/ };
    virtual void Put(ulong pos, void* src, ulong size)
    {
        //throw "read only, disablBe write feature.";
    }

    virtual void Clear()
    {
        m_posRead = 0;
        m_posWrite = 0;
    }

private:
    CReadBufferHelper(const CReadBufferHelper& r) = delete;
    CReadBufferHelper& operator=(const CReadBufferHelper& r) = delete;
};

class CWriteBufferHelper : public IStreamBuffer
{
public:
    CWriteBufferHelper(IArchive* pData, ulong size)
    {
        m_pArchive = pData;
        m_pBuff = (char*)m_pArchive->GetHead();
        m_buffSize = size;
        m_bBad = false;
    }

    CWriteBufferHelper(void* pData, ulong size)
    {
        m_pArchive = NULL;
        m_pBuff = (char*)pData;
        m_buffSize = size;
        m_bBad = false;
    }

    ~CWriteBufferHelper() { if (m_pArchive) m_pArchive->Release(); }

    virtual void _Resize(ulong newSize) { m_bBad = true; };
    virtual void Read(void* src, ulong size) { /*throw "write only, disable read feature.";*/ }

    virtual void Clear() 
    {
        m_posRead = 0;
        m_posWrite = 0;
        m_validateSize = 0;
        m_bBad = false;
    }

private:
    CWriteBufferHelper(const CWriteBufferHelper& r) = delete;
    CWriteBufferHelper& operator=(const CWriteBufferHelper& r) = delete;

    IArchive*   m_pArchive;
};

typedef CBuffer<512>	XBuffer;

#define BUILD_VECTOR_PACKET(buf, vec)\
{\
    ulong count = (ulong)vec.size(); \
    buf << count; \
    if (count > 0)\
    {\
        ulong szStruct = sizeof(vec[0]); \
        buf << szStruct; \
        buf.Write((void*)vec.data(), count * szStruct); \
    }\
}

#define BUILD_VECTOR_PACKET_CLASS(buf, vec)\
{\
    ulong count = (ulong)vec.size(); \
    buf << count; \
    for (auto& v : vec) \
    {\
        buf << v;\
    }\
}

#define LOAD_VECTOR_PACKET(buf, vec)\
{\
    vec.clear(); \
    ulong count = 0; \
    buf >> count; \
    if (count > 0) \
    {\
        ulong szStruct = 0; \
        buf >> szStruct; \
        if (szStruct == sizeof(vec[0])) \
        {\
            vec.resize(count); \
            buf.Read((void*)vec.data(), count * szStruct); \
        }\
    }\
}

#define LOAD_VECTOR_PACKET_CLASS(buf, vec)\
{\
    vec.clear(); \
    ulong count = 0; \
    buf >> count; \
    if (count > 0) \
    {\
        vec.resize(count); \
        for (ulong i = 0; i < count; ++i)\
        {\
            buf >> vec[i];\
        }\
    }\
}

#define BUILD_ARRAY_PACKET(buf, arr, count)\
{\
    buf << count; \
    if (count > 0)\
    {\
        ulong szStruct = sizeof(arr[0]); \
        buf << szStruct; \
        buf.Write((void*)&arr[0], count * szStruct); \
    }\
}

#define LOAD_ARRAY_PACKET(buf, arr, arrSize)\
{\
    ulong count = 0; \
    buf >> count; \
    if (count > 0 && arrSize == count)\
    {\
	    ulong szStruct = 0; \
	    buf >> szStruct; \
        if (szStruct == sizeof(arr[0]))\
        {\
	        buf.Read((void*)&arr[0], szStruct * count); \
        }\
    }\
}

 

3.3 帶內存管理的消息接口

內存獲取不是每次都是從堆上申請,而是在內存池中獲取一個之前開闢好的內存單元,沒有就先申請,相同的網絡消息數據大小往往都是相同,可以重複利用,避免過多內存碎片。

 

3.4 優缺點比較

方法一

優點:

  1. 協議組合方便
  2. 可以檢測內存是否越界
  3. 可以統計待發送的消息

缺點

  1. 沒有對內存進行復用,容易產生大量內存碎片
  2. 對一些一些容器數據寫入不太方便

方法二

優點:

  1. 可以通過數據流的形式寫入或讀取數據
  2. 內容可以自動擴展
  3. 一些容器數據的寫入和讀取很方便

缺點:

  1. 沒有對內存進行復用,容易產生大量內存碎片
  2. 寫入的數據超過當前申請的內存

方法三:

優點:

  1. 支持內存複用
  2. 支持流結構

缺點:

  1. 實現較爲複雜

 

四、擴展延伸

在發送消息的時候,數據從應用層發送給網絡層需要一次拷貝,網絡層將數據發送給緩衝區又進行了一次拷貝,能不能減少一次拷貝呢?

這需要網絡層支持,在網絡層中將數據拷貝到緩衝區時,定義好接口,只傳入指針和數據大小,在數據發送完之後,我們在將數據進行回收,可以達到減少一次拷貝。結合方法三,在回收的時候可以對數據進行判斷,如果是內存池中的數據,將其發到內存中,或者數據塊較大,也將它發到內存池中的大數據中,大數據數據大小我們自己定義,一般定義爲大於16k。

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章