游戲里面的數據,最重要的就是如何組織,常見的包結構就是type+len+data這樣的結構
//所有包的基類型
struct BasePack
{
unsigned short type; //類型
unsigned short len; //長度
BasePack();
unsigned short size();
};
//////////////////////////////////////////////////////////////////////////
//比如這個是登錄包結構
struct LoginPack :public BasePack
{
char username[15];
char password[10];
LoginPack();
unsigned short size();
};
//..還有很多類型的包結構,自己去擴展
//下面這個包結構就很特別了:
這個struct 結構能保存變長的子結構體,也就是結構體的容器,如果一次要發送大量查詢的數據,可以用這個容器來裝載。結構大致如下:
//////////////////////////////////////////////////////////////////////////
//集合包,這是個很特殊的包,里面的數據是變長的(buffer 將作為子包的首指針)
//當然也有個上限,只不過和StringPack一樣,每次發送的數據量不固定,并不一定是包體的長度,
//使用這個包的注意事項:1.如果用tcp,總長不要超過4096, 如果用udp總長不要超過1024(安全指數)
// 2.一般一個包不會共享給多個線程來處理,而且都是臨時拼發,并未考慮線程安全
struct CollectionPack:public BasePack
{
unsigned short mSubPackCount; //子包的數量
char buffer[4096]; //最大限度的包長度,做緩沖
CollectionPack();
//清除CollectionPack里面的內容
void clear();
//得到子包的數量
unsigned short getSubpackCount();
//添加子包,添加是否成功,集合包的大小,默認不超過MTU的大小,當然如果是TCP傳輸,則沒有這個限制,最大可以是4096
bool append(BasePack &pack, unsigned short maxsize=1500);
//本集合包的總長度
unsigned short size();
//重載數組運算符,這樣就可以數組迭代的方式訪問子包了,不過用索引迭代沒有next迭代的效率高
BasePack *operator[](int idx);
//p迭代的指針,返回當前取出來的包的指針使用方法如下:
// BasePack *p = 0;
//while(collection.next(&p))
//{
// p就是當前你找到的包了
//}
BasePack * next(BasePack ** p);
};
CollectionPack::CollectionPack()
{
memset(buffer,0, 4096);
type = COLLECTION_PACK;
mSubPackCount = 0;
}
//清除CollectionPack里面的內容
void CollectionPack::clear()
{
mSubPackCount = 0;
memset(buffer,0, 4096);
}
//得到子包的數量
unsigned short CollectionPack::getSubpackCount()
{
return mSubPackCount;
}
//添加子包,添加是否成功
bool CollectionPack::append(BasePack &pack, unsigned short maxsize)
{
unsigned int currentlen = size(); //當前整個包的長度
unsigned int psize = pack.size(); //即將要加入的包的長度
if(psize + currentlen > maxsize || psize + currentlen > SESSION_BUFFER_LONGTH)
{
return false; //不能夠再裝了
}
int len = size() - 6;
memcpy(buffer + len, &pack, pack.size());
//StringPack *p = (StringPack *) (buffer + len);
mSubPackCount ++;
return true;
}
//本集合包的總長度
unsigned short CollectionPack::size()
{
len = 0;
for(unsigned short i = 0; i < mSubPackCount; i ++)
{
BasePack * p = (BasePack *) (buffer + len);
len += p->size();
}
len += 6; //(type len SubPackNum 共6個字節)
return len;
}
//重載數組運算符,這樣就可以數組迭代的方式訪問子包了,不過用索引迭代沒有next迭代的效率高
BasePack * CollectionPack::operator[](int idx)
{
if(idx < 0 || idx >=mSubPackCount)
{
return 0; //下標越界
}
int ln = 0;
for(unsigned short i = 0; i < mSubPackCount; i ++)
{
BasePack * p = (BasePack *) (buffer + ln);
if(idx == i)
return p;
else
ln += p->size();
}
return 0;
}
//p迭代的指針,返回當前取出來的包的指針使用方法如下:
// BasePack *p = 0;
//while(collection.next(&p))
//{
// p就是當前你找到的包了
//}
BasePack * CollectionPack::next(BasePack ** p)
{
char *cur = 0;
if((*p)==0)
cur = buffer;
else
cur = (char *)(*p);
//指針后移,定位到下一個包的位置
(*p)=(BasePack *)(cur + ((BasePack *)cur)->size());
if((*p)->type == 0)
return 0; //沒有類型為0類型的包,如果為0,顯然是到末尾了
else
return (*p);
}
有了以上這個容器,要把一些小包組合起來一起發送就非常方便了,但是組合的時候,還是要考慮不能超出上限范圍的
不過在真實的游戲服務器里面,包不一定是采用結構體的方式來發送的,有的是把對象串行化成為字節流的方式來發送。
我覺得這樣比較麻煩,要encoding decoding,效率調試都不方便,直接法結構體,高效,簡單,可就是不太安全。