抱歉这篇文章出现这么迟。

当一个数据包出现在网络上的时候,例如收到了返回值不为零的数据,涉及了三个步骤来处理它。

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处理数据包类型的函数在此调用
}

有用的注释:

我们转化数据包的数据为一个适当的指针类型,如果我们真的要创建这个结构体时,可以避免拷贝的开销。然而在这个例子中,你修改了结构体中的任何数据,数据包中的内容也会被修改。这个可能不是我们想要的。作为一个服务器,在中转数据的时候要注意,可能会引起未知的错误。

尽管断言不是必要的,当我们发送一个数据包时,如果我们指定了错误的标识符或者错误包长度,断言对于捕获非常难发现的bug是非常有效的。

当某些人设法发送非法的长度或者类型为了让客户端或者服务器宕机时,if语句是非常有效的。在实践中,这种情况没有发生过,但是没有发生过并不代表是安全的。

接收到一个字节流

如果你最初发送的是个字节流,则需要创建一个字节流,按照我们写入的顺序来解析数据。我们创建一个使用数据和数据包长度的字节流。如果前面我们使用的是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 networlId
    myBitStream.Read(systemAddress); // 在结构体中这个是SystemAddress systemAddress
}

3.释放数据包

RakPeerInterface实例中得到的数据包,传递给DeallocatePacket(Packet* packet)函数,从而释放该数据包。