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