接收数据包
当一个数据包到来时,例如Receive返回一个非零,处理这个数据包需要三步:
1. 确定数据包类型。使用如下的代码可以返回这个类型值。
unsigned char GetPacketIdentifier(Packet *p)
{
if ((unsigned char)p->data[0] == ID_TIMESTAMP)
return (unsigned char) p->data[sizeof(unsigned char) + sizeof(unsigned long)];
else
return (unsigned char) p->data[0];
}
2. 处理数据
接收结构
如果你原始发送一个结构体,可以按照如下的方式转化出这个结构体:
if (GetPacketIdentifier(packet)==/* 在这里使用赋值的数据包标识符 */)
DoMyPacketHandler(packet);
// 可以将这个函数放到任何位置,在处理游戏的状态类中比较好
void DoMyPacketHandler(Packet *packet)
{
// 将数据转化为适合类型的结构体
MyStruct *s = (MyStruct *) packet->data;
assert(p->length == sizeof(MyStruct)); // 如果传输的是结构体这块这样处理比较好
if (p->length != sizeof(MyStruct))
return;
// 在这里调用函数处理结构体 MyStruct *s
}
使用注释:
1. 将数据包的数据转换为适合类型结构体的指针,这样可以避免复制数据造成的开销。然后在这种情况下,如果你修改了结构体中的任何数据,数据包中的数据也会被修改掉。当然了,这种情况不是我们想要看到的。作为一个服务器,在中继数据的时候要多加注意,因为中继数据会引起未知的Bugs。
2. 尽管assert不是特别必要,但是如果我们对标识符赋值错误了,assert对于发现bug非常有用。
3.在有人要发送一个大小或类型无效的数据包,使得服务器或客户端崩溃情况时,if语句就显得非常有用。在实践中,没有发生过这样的事情,虽然没有出现过也不能说明是安全的。
接收一个位流数据(BitStream)
如果你最初发送的是一个Bitstream,那就需要创建一个BitStream,按照我们的写入顺序来解析数据。使用数据和数据包的长度来创建一个BitStream。我们写入数据的时候,使用的是Write函数,那么就使用Read函数读取数据。如果前面使用的WriteCompressed函数,那读取数据就要使用ReadCompressed函数。如果我们条件性的写入任何数据,依据这个逻辑分支。在接下来的例子中给出了处理在Creating packets中的地雷的数据:
void DoMyPacketHandler(Packet *packet)
{
Bitstream myBitStream(packet->data, packet->length, false); // false指定不拷贝数据,提高效率
myBitStream.Read(useTimeStamp);
myBitStream.Read(timeStamp);
myBitStream.Read(typeId);
bool isAtZero;
myBitStream.Read(isAtZero);
if (isAtZero==false)
{
x=0.0f;
y=0.0f;
z=0.0f;
}
else
{
myBitStream.Read(x);
myBitStream.Read(y);
myBitStream.Read(z);
}
myBitStream.Read(networkID); // 在结构体中这里是 NetworkID networkId
myBitStream.Read(systemAddress); // 在结构体中这里是SystemAddress systemAddress
}
3. 通过将数据包传递给RakPeerInterface实例的DeallocatePakcet(Packet *packet)释放数据包。
By 北洋小郭