服务器框架——网络消息协议

一、介绍

用于服务器和客户端应用层之间的协议,通过定义的协议,可以将接收的数据转发给相应的模块,相应模块可以将数据按照约定的结构进行解析。

 

二、协议体

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。

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值