游戏里面的数据,最重要的就是如何组织,常见的包结构就是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,效率调试都不方便,直接法结构体,高效,简单,可就是不太安全。