一、介紹
用於服務器和客戶端應用層之間的協議,通過定義的協議,可以將接收的數據轉發給相應的模塊,相應模塊可以將數據按照約定的結構進行解析。
二、協議體
2.1 消息碼定義
消息碼分爲:分類消息碼,主消息碼,子消息碼
2.2 消息碼作用
消息碼用於消息轉發,將消息一層一層傳給對應模塊。消息碼作用介紹:
- 分類消息碼,用於網關服轉發消息,網關服通過分類消息碼,將消息發送給對應的服務器
- 主消息碼,將消息轉發給對應模塊
- 子消息碼,一個模塊中定義的消息碼,用於處理一個具體某個邏輯
在這三種模塊中,我們還可以根據實際業務需要添加新的消息碼類型。例如,房間插件中的牌桌消息,牌桌消息是與牌桌遊戲相關的一類消息,它可以根據不同遊戲定義各自的消息類型,爲了方便擴展,我們可以將這類消息共用同一個【子消息碼】,然後在【子消息碼】後面再加一個【遊戲消息碼】,這樣不同遊戲可以各自定義自己的遊戲消息,遊戲消息碼爲:分類消息碼(遊戲服),主消息碼(房間插件),子消息碼(房間數據),某個遊戲消息碼(具體遊戲消息碼),數據。
說明:廣場服和遊戲服,在收到網關服消息,除了登錄消息外所有消息都是發送到對應插件中,插件自己處理各種的消息。每個插件都會定義一個自己的消息碼,網關服代理通過主消息碼將消息發送給有關的插件。
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 優缺點比較
方法一
優點:
- 協議組合方便
- 可以檢測內存是否越界
- 可以統計待發送的消息
缺點
- 沒有對內存進行復用,容易產生大量內存碎片
- 對一些一些容器數據寫入不太方便
方法二
優點:
- 可以通過數據流的形式寫入或讀取數據
- 內容可以自動擴展
- 一些容器數據的寫入和讀取很方便
缺點:
- 沒有對內存進行復用,容易產生大量內存碎片
- 寫入的數據超過當前申請的內存
方法三:
優點:
- 支持內存複用
- 支持流結構
缺點:
- 實現較爲複雜
四、擴展延伸
在發送消息的時候,數據從應用層發送給網絡層需要一次拷貝,網絡層將數據發送給緩衝區又進行了一次拷貝,能不能減少一次拷貝呢?
這需要網絡層支持,在網絡層中將數據拷貝到緩衝區時,定義好接口,只傳入指針和數據大小,在數據發送完之後,我們在將數據進行回收,可以達到減少一次拷貝。結合方法三,在回收的時候可以對數據進行判斷,如果是內存池中的數據,將其發到內存中,或者數據塊較大,也將它發到內存池中的大數據中,大數據數據大小我們自己定義,一般定義爲大於16k。