C++游戏服务器框架笔记(一)_封装数据包类

笔记目录导航

C++游戏服务器框架笔记(一)_封装数据包类

C++游戏服务器框架笔记(二)_封装Socket类

C++游戏服务器框架笔记(三)_封装ByteBuffer类

......

【前要注明】:

        游戏服务器与客户端的通信是通过socket网络接口按照既定的协议发送、接受数据包传递数据,那么怎样组织一个数据包是非常重要的环节,这里封装了一个简单的DataPack数据包类,较为方便打包和解析协议数据。

        数据包按照以下格式组织:

              包头       数据长度     协议数据

        数据包的结构是比较简单的 包头比较简陋 也没有校验,后面会再完善优化,下面大概说下DataPack中存储数据的操作:

         DataPack有四个核心指针:

                 m_starptr  指向数据缓冲区在内存中的起始地址         

                 m_endptr  指向数据缓冲区在内存中的结束地址

                 m_dataptr  指向数据在缓冲区中的数据尾部地址,随数据的大小改变         

                 m_offset  数据偏移指针,方便操作读取数据 

         DataPack封装构造数据的流程:

                 构造指定数据指定长的数据包,则动态分配指定长度的内存创建数据缓冲区

                 并按照上述说明,初始化类核心指针成员。构造完成后的结构如下图所示:

C++游戏服务器框架笔记(一)_封装数据包类_第1张图片

         DataPack数据缓冲区的动态扩充:

                写入数据的时候如果初始构造的数据包缓冲区的大小不足,则需要动态扩大缓冲区的内存空间,通常的做法是增大一倍空间,不过我这里是只增大写入数据的大小,动态扩大缓冲区内存使用C库函数realloc():

                  函数声明:void *realloc(void *ptr, size_t size)

                  函数描述:先判断当前的指针是否有足够的连续空间,如果有,扩大mem_address指向的地址,并且将                              mem_address返回,如果空间不够,先按照newsize指定的大小分配空间,将原有数据从头到尾拷贝到新分配的内存区域,          而后  释放原来mem_address所指内存区域(注意:原来指针是自动释放,不需要使用free),同时返回新分配的内存区域          的首地址。即重新分配存储器块的地址。

                 数返回值:如果重新分配成功则返回指向被分配内存的指针,否则返回空指针NULL。

                重新分配缓冲据的内存空间后,需要重新初始化四个核心指针,完成DataPack内存缓冲区的动态扩充工作

        下面是DataPack的头文件:


#ifndef  _DATAPACK_H_
#define  _DATAPACK_H_

#include 
#include 
#include 
#include 

//包头长度
#define HEAD_PACK_LEN 4

class DataPack
{
public:
	DataPack();
	DataPack(void* buffer, int len);
	~DataPack();

	//反序列化字符串为DataPack
	void FromBuffer(void* buffer, int len);
	//清空缓冲区
	void CleanBufer(void);


	//设置数据缓冲区的大小
	void SetSize(int size);
	//获取数据缓冲区中的数据首地址
	const char* GetBuffer(void);
	//获取数据缓冲区大小
	int GetBufferLength(void);
	//设置指针偏移,读取出一个字段数据后需要设置,更新读取位置
	void SetOffset(int pos);

	//写入unsigned char类型数据,并移动m_offset
	void WriteByte(unsigned char c);
	//写入short类型数据,并移动m_offset
	void WriteShort(short s);
	//写入int类型数据,并移动m_offset
	void WriteInt(int i);
	//写入long类型数据,并移动m_offset
	void WriteLong(long l);
	//写入float类型数据,并移动m_offset
	void WriteFloat(float f);
	//写入double类型数据,并移动m_offset
	void WriteDouble(double d);
	//写入其他DataPack读出的数据到到数据缓冲区
	void WriteBuffer(void* buff, int size);
	//写入字符串数据,len为长度,默认-1接口内部计算长度
	void WriteString(const char* str, int len = -1);
	//写入包的长度,即用新的包的长度重新填充包头后面的datalen内存空间
	void WritePackLen(int len);

	//读出Short类型数据,并移动m_offset
	short ReadShort(void);
	//读出Int类型数据,并移动m_offset
	int ReadInt(void);
	//读出unsigned char类型数据,并移动m_offset
	unsigned char ReadByte(void);
	//读出Float类型数据,并移动m_offset
	float ReadFloat(void);
	//读出Double类型数据,并移动m_offset
	double ReadDouble(void);
	//读出Long类型数据,并移动m_offset
	long ReadLong(void);
	//读出数据缓冲区数据到buffer指针(memcopy), 并移动m_offset
	int ReadBuffer(void* buffer);
	//读出数据缓冲区数据到buffer指针(memcopy),并加上字符串结束符'\0', 并移动m_offset
	int ReadString(char* pstr);
	//读出数据缓冲区中字符串,返回字符指针,使用完需要手动释放
	char* ReadString(int& out_size);
	//获取包数据的长度datalen
	int GetPackLen(void);
	//获取缓冲区大小
	int GetBufferSize(void);
	//释放数据缓冲区内存空间
	void FreeDack(void);
private:
	//检查当前数据缓冲区的空间是否可以添加size大小数据,如果不足则动态扩充(调用SetSize)
	void CheckCapacity(int size);
private:
	//数据缓冲区起始地址
	char* m_starptr;
	//数据缓冲区结束地址
	char* m_endptr;
	//数据缓冲区中数据的结束地址
	char* m_dataptr;
	//读取数据的偏移指针
	char* m_offset;
};
#endif

        下面是DataPack的cpp文件:


#include "DataPack.h"

/* 协议头,每个数据包中前两个字节是数据头,
 后四个字节是数据长度(包含协议头长度和数据长度)*/
static int DATA_BLOCK = 128;

DataPack::DataPack() {
	int size = HEAD_PACK_LEN + DATA_BLOCK;
	m_starptr = (char*)malloc(size);
	m_endptr = m_starptr + size;
	memset(m_starptr, 0, size);
	m_dataptr = m_starptr+ HEAD_PACK_LEN;
	m_offset = m_starptr + HEAD_PACK_LEN;
}

DataPack::DataPack(void* buffer, int len)
{
	m_starptr = (char*)malloc(len);
	m_endptr = m_starptr + len;
	memcpy(m_starptr, buffer, len);
	m_dataptr = m_starptr + HEAD_PACK_LEN + len;
	m_offset = m_starptr + HEAD_PACK_LEN;
}

DataPack::~DataPack()
{
	self:FreeDack();
}

void DataPack::FreeDack(void)
{
	free(m_starptr);
	m_endptr = NULL;
	m_dataptr = NULL;
	m_offset = NULL;
}

void DataPack::FromBuffer(void* buffer, int len)
{
	if (this->GetBufferSize() < len)
		this->SetSize(len);

	memset(m_starptr, 0, this->GetBufferSize());
	memcpy(m_starptr, buffer, len);
	m_dataptr = m_starptr + len;
	m_offset = m_starptr + HEAD_PACK_LEN;
}

void DataPack::CleanBufer(void) {
	m_dataptr = m_starptr;
	memset(m_starptr, 0, this->GetBufferSize());
	m_offset = m_starptr + HEAD_PACK_LEN;
}

void DataPack::SetSize(int size)
{
	//不能缩小包内存
	if (size <= m_endptr - m_dataptr)
		return;

	//保存现在的各个地址位置偏移大小
	int datalen = m_dataptr - m_starptr;
	int offset = m_offset - m_starptr;

	//动态扩充数据缓冲区内存并还原各个地址相对m_starptr偏移位置
	m_starptr = (char*)realloc(m_starptr, size);
	m_endptr = m_starptr + size;
	m_dataptr = m_starptr + datalen;
	m_offset = m_starptr + offset;
}

const char* DataPack::GetBuffer(void)
{
	return m_starptr;
}

int DataPack::GetBufferLength(void)
{
	return m_dataptr - m_starptr;
}

void DataPack::SetOffset(int pos)
{
	m_offset = m_starptr + pos;
}

void DataPack::WriteByte(unsigned char c)
{
	int size = sizeof(unsigned char);
	CheckCapacity(size);
	memcpy(m_dataptr, &c, size);
	m_dataptr += size;
	WritePackLen(size);
}

void DataPack::WriteShort(short s)
{
	int size = sizeof(short);
	CheckCapacity(size);
	memcpy(m_dataptr, &s, size);
	m_dataptr += size;
	WritePackLen(size);
}

void DataPack::WriteInt(int i)
{
	int size = sizeof(int);
	CheckCapacity(size);
	memcpy(m_dataptr, &i, size);
	m_dataptr += size;
	WritePackLen(size);
}

void DataPack::WriteLong(long l)
{
	int size = sizeof(long);
	CheckCapacity(size);
	memcpy(m_dataptr, &l, size);
	m_dataptr += size;
	WritePackLen(size);
}

void DataPack::WriteFloat(float f)
{
	int size = sizeof(float);
	CheckCapacity(size);
	memcpy(m_dataptr, &f, size);
	m_dataptr += size;
	WritePackLen(size);
}

void DataPack::WriteDouble(double d)
{
	int size = sizeof(double);
	CheckCapacity(size);
	memcpy(m_dataptr, &d, size);
	m_dataptr += size;
	WritePackLen(size);
}

void DataPack::WriteBuffer(void* buff, int size)
{
	CheckCapacity(size);
	WriteInt(size);
	memcpy(m_dataptr, buff, size);
	m_dataptr = m_dataptr + size;
	WritePackLen(size);
}

void DataPack::WriteString(const char* str, int len)
{
	if (len == -1)
		len = strlen(str);
	WriteInt(len);
	CheckCapacity(len);
	memcpy(m_dataptr, str, len);
	m_dataptr = m_dataptr + len;
	WritePackLen(len);
}

void DataPack::WritePackLen(int len)
{
	int oldlen = *((int*)(m_starptr)) + len;
	memcpy(m_starptr, &oldlen, sizeof(int));
}

void DataPack::CheckCapacity(int size)
{
	if (m_endptr - m_dataptr > size) {
		return;
	}

	int add_size = DATA_BLOCK;
	while (m_endptr - m_dataptr + add_size < size) {
		add_size += DATA_BLOCK;
	}

	SetSize(GetPackLen() + add_size);
}

short DataPack::ReadShort(void)
{
	short data = *((short*)m_offset);
	m_offset += sizeof(short);
	return data;
}

int DataPack::ReadInt(void)
{
	int data = *((int*)m_offset);
	m_offset += sizeof(int);
	return data;
}

unsigned char DataPack::ReadByte(void)
{
	unsigned char data = *((unsigned char*)m_offset);
	m_offset += 1;
	return data;
}

float DataPack::ReadFloat(void)
{
	float data = *((float*)m_offset);
	m_offset += sizeof(float);
	return data;
}

double DataPack::ReadDouble(void)
{
	double data = *((double*)m_offset);
	m_offset += sizeof(double);
	return data;
}

long DataPack::ReadLong(void)
{
	long data = *((long*)m_offset);
	m_offset += sizeof(long);
	return data;
}

int DataPack::ReadBuffer(void* buffer)
{
	int size = ReadInt();
	memcpy(buffer, m_offset, size);
	m_offset += size;
	return size;
}

int DataPack::ReadString(char* pstr)
{
	int size = ReadInt();
	memcpy(pstr, m_offset, size);
	*(pstr + size + 1) = '\0';
	m_offset += size;
	return size;
}

char* DataPack::ReadString(int& out_size)
{
	int size = ReadInt();
	char* pstr = (char*)malloc(sizeof(char) * size + 1);
	memcpy(pstr, m_offset, size);
	*(pstr + size + 1) = '\0';
	m_offset += size;
	return pstr;
}

int DataPack::GetPackLen(void)
{
	return *((int*)m_starptr);
}

int DataPack::GetBufferSize(void)
{
	return m_endptr - m_starptr;
}

         将协议数据打包成数据包后,调用GetBuffer()和GetBufferLength() 拿到数据和数据长度,然后通过socket接口发送     

         下面是发送端封装数据包例子(代码片段):


void test_data_pack()
{
    //封包
	DataPack pack;
	pack.SetSize(256);
	pack.WriteInt(1997);
	pack.WriteInt(1015);
	pack.WriteDouble(99.99);
	pack.WriteLong(9999);

	struct Test {
		int num;
		float weight;
		char data[36];
	};

	Test t = { 1, 78.8, "So live a life you will remember" };
	pack.WriteBuffer(&t, sizeof(t));
	pack.WriteString("就活出你的人生 这回忆值得你铭记");

    //反序列化,读取数据
	DataPack * pack1 = new  DataPack();
	pack1->FromBuffer((void*)pack.GetBuffer(), pack.GetBufferLength());
	cout << pack1->GetPackLen() << endl;
	cout << pack1->ReadInt() << endl;
	cout << pack1->ReadInt() << endl;
	cout << pack1->ReadDouble() << endl;
	cout << pack1->ReadLong() << endl;

	Test t1;
	memset(&t1, 0, sizeof(t1));
	cout << pack1->ReadBuffer(&t1) << endl;
	cout << t1.num << endl;
	cout << t1.weight << endl;
	cout << t1.data << endl;

	int out_size;
	char* pstr = pack1->ReadString(out_size);
	cout << pstr << endl;
	free(pstr);
}

         下面是接收端解析数据包打印数据结果:

        C++游戏服务器框架笔记(一)_封装数据包类_第2张图片

你可能感兴趣的:(C++游戏服务器开发,服务器,c++,游戏,c++服务器开发,C++游戏服务器开发)