网络游戏中消息包结构

一 


NetPacket:

NetPacket(int buffersize,NetPacketPool* pool,bool scal=true,bool policy=false)
{
//构造函数,
if(buffersize<7)
buffersize = 7;//消息包最小7字节 1+2+4 type,proc,size
m_buffer = new Char[2048];//默认2K
m_buffer += 7; //指向消息体
m_bufferSize = buffersize-7;//有效消息大小,消息包总大小
m_pool = pool;//这里传进池子指针,为了后面回收消息包使用 
m_scalable = scal;//默认可扩展,如果比如写入大小超过2K了,那么需要增加这包的大小,重新申请大空间
m_policy = policy;//不管、
reset();//一些成员变量初始化一下


}
void reset()
{
m_rOffset = 0;//读偏移0 起点
m_wOffset = 0;//写偏移0 起点
m_rOverFlow = false;
m_wOverFlow = false; //这两个表示读写偏移是否超过了消息包有效长度
m_proc = 0;//协议号,
m_size = 0;//消息包总大小
}
readInt readDouble,writeInt writeDouble等都使用模板方法
template<typename T>
T read()
{
if(m_rOverFlow)
return T(0);//消息包已经读完了,后面的数据肯定是乱的不能再读啦
if(m_rOffset + sizeof(T) > m_wOffset)
{
//说明不够读了,写偏移比如10,现在读偏移是8,你要读取int,肯定是不可以的,读了就出错
m_rOverFlow = true;//丢掉数据也不能读 正常情况不会出现,出现肯定哪里代码有问题了,比如数据解析不对
return T(0);
}
m_rOffset += sizeof(T);//走到这里说明这次读取是没有问题的,读偏移后移
return *((T*)(m_buffer+m_rOffset-sizeof(T)));//返回对应类型的有效数据,
//m_buffer是消息体的起始位置,加上读的偏移,减去刚才的,就是这次需要读的数据地址的起点,
//括号里面指针类型转化下,否则知道位置,但不知道我该取到哪里为止,
//比如你是int,那么我就从这里取4字节
//因此转化成int* 的指针,然后通过指针取,就能取到正确的int数据
}
template<typename T>
T write(T val)
{
if(m_wOverFlow)
return;
if(m_wOffset+sizeof(T)>m_buffersize)
{
//写偏移再加上这次要写的大小,超过了消息包的大小(2k),不行了
if(m_scal)
{
//如果消息包是弹性包,支持扩容那么我重新申请大空间
char* buffer = new char[m_buffersize*2+7];//每次空间不够申请当前大小的2倍,类似vector
memcpy(buffer,m_buffer-7,m_buffersize+7);//m_buffer指向消息体,-7指向消息头部,拷贝整个消息包的内容到新的内存
//buffer现在所指向的位置之后就是原来的消息,
m_buffer -= 7;
//因此m_buffer-7指针所指的那片空间可以释放了(消息头+消息体)
delete[] m_buffer;
m_buffer = buffer+7;//释放完后,把m_buffer重新指向新内存空间里面的消息体
m_buffersize = m_buffersize*2;//新的消息体可用大小
}
if(m_wOffset+sizeof(T)>m_buffersize)
{
//重新申请后还是不够,那么就退出吧,不要写了
m_wOverFlow = true;
return;
}
}
//这里说明可以写,那就找到些偏移的位置,对应类型的空间,写入值,
*((T*)(m_buffer+m_wOverFlow)) = val;
//写偏移后移
m_wOverFlow += sizeof(T);
}




{
头:1+2+4,
char* m_buffer,消息体位置,消息头往后7字节
buffersize 消息包总大小包括头和尾
m_buffersize 消息体大小 m_buffersize = buffersize - 7;

}
void destroy()
{
if(m_pool!=null)
m_pool->push(this);//消息池见下面
else
delete this;
}


当然上面只是一个简单的网络消息包的构成,以及读写,
游戏中需要发包和收包当然不能 需要了就new一个,用完之后delete
很耗费时间,而且很多内存碎片,需要频繁的使用内存的都可以用池子
简单的NetPacketPool,如下



实际上池子存的不是消息包,而是一组消息包的内存地址,就是开辟了一对空间,然后把钥匙全部放在池子里
池子里放的是这些钥匙,不是空间

//SafeQueue:具体暂时不管,它就是一个自己会处理好互斥锁的一个容器,
//外部访问的时候不用考虑多线程同步的问题
typedef SafeQueue<NetPacket*> NetPacketList;
NetPacketList m_pool; //池子

构造函数
NetPacketPool(int size,int initcount,int maxcount)
:m_bufferSize(size)//池子里面的消息包的大小,默认2K
,m_initCount(initcount)//池子初始大小
,m_maxCount(maxcount)//池子最大存多少个消息包
{
for(int 9=0;i<m_initCount;i++)
{
NetPacket* netpacket = new NetPacket(m_bufferSize+7,this);//NetPacket的构造函数请看上面
m_pool.push(netpacket);//m_pool才是这个池子
}
}
~NetPacketPool()
{
//析构当然就是用池子里面的钥匙,拿了钥匙把占的内存都释放掉,空间重回系统的怀抱
NetPacket* pack = null;
while((packet=m_pool.pop())!=NULL)
{
delete pack;
pack = null;
}
}
NetPacket* pop()
{
//有了池子,外部使用需要NetPakcet的时候就不用自己造一个了,池子里面取就行了
NetPacket* pack = m_pool.pop();
if(pack == null)
{
//空的,没取到,有可能,可能初始的不够,或者需要的太多,不够用了
pack = new NetPacket(m_bufferSize+7,this);//不够就new 不会空手而归
}
pack->reset();//重置一下,毕竟池子谁都用过,里面可能还是之前的数据
return pack;
}
void push(NetPacket* pack)
{
//用完之后,自己不要delete 而是放回池子里面,其他地方还可以再用
if(pack != NULL && pack->subRefCount() == 0)
{
//pack不为空,而且现在没人用,引用计数,
//否则回收了指向的空间,其他地方还在用这个指针,程序崩溃
if(m_pool.size()>m_maxCount)
{
//比如池子最大2000,但是很多地方用,当时pop的时候不够用可能new了很多
//如果我都往里面放,那么池子可能非常大,几千几万,那就非常耗费内存
delete pack;
pack=null;//所以直接清掉,不放回池子
}
else
{
m_pool.push(pack);//正常范围内,塞进池子
}
}
}


简单的池子就是这样,下面简单的使用场景


例如GameServiceManager,里面
成员变量 NetPacketPool m_bufferPool;
m_bufferPool(2048-7,0,2000);
参照上面的NetPacketPool的构造函数,
这个池子里面消息包的长度都是2k,池子初始是空的,池子最多放2000个消息包
当我需要制造一个NetPacket的时候
那么
NetPacket* packet = m_bufferPool.pop();
if(packet!=null)
{
packet->setType(packType);
packet->setProc(proc);
packet->writeInt(xxx);
packet->writeDouble(xxx);
packet->setSize(packet->getWOffset());
gameServer->sendPacket();//Tcp发消息
packet->destroy();

}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值