解析可变大小的数据包

Parsing variable-sized packets

我正在开发一款多人游戏,但我在解析来自连接的数据包的方式时遇到了问题。当我调试游戏时,它以较低的性能运行并且接收到数据包,当我没有调试时,数据包没有完全接收并且没有调用 ParsePacket 方法。

我的数据包结构是这样的:

2 Bytes Short Command, 2 Bytes Short Payload Size, (Optional) Payload Bytes

IInputStream inputStream = null;
DataReader dataReader = null;

byte[] data = new byte[1024];
IBuffer buffer = data.AsBuffer();

try
{
    inputStream = StreamSocket.InputStream;

    dataReader = DataReader.FromBuffer(buffer);
    dataReader.InputStreamOptions = InputStreamOptions.Partial;
    dataReader.ByteOrder = ByteOrder.LittleEndian;

    while (connected)
    {
        await inputStream.ReadAsync(buffer, 1024, InputStreamOptions.Partial);
        Debug.WriteLine("Buffer " + buffer.Length);

        if (buffer.Length >= PacketHeaderSize)
        {
            short command = dataReader.ReadInt16();
            short payloadSize = dataReader.ReadInt16();
            byte[] payload = null;

            if (payloadSize == 0)
            {
                UpdateBuffer(buffer, (uint)(PacketHeaderSize + payloadSize));

                Packet packet = new Packet(command, payloadSize, payload);
                ParsePacket(packet);
            }
            else if (payloadSize > 0)
            {
                if (buffer.Length >= (PacketHeaderSize + payloadSize))
                {
                    payload = new byte[payloadSize];
                    dataReader.ReadBytes(payload);

                    UpdateBuffer(buffer, (uint)(PacketHeaderSize + payloadSize));

                    Packet packet = new Packet(command, payloadSize, payload);
                    ParsePacket(packet);
                }
            }
        }
    }
}
catch (Exception e) {
    // ...
}

private void UpdateBuffer(IBuffer buffer, uint bytesRead)
{
    if (buffer.Length > bytesRead)
    {
        byte[] bufferBytes = new byte[buffer.Length - bytesRead];
        System.Buffer.BlockCopy(buffer.ToArray(), (int)bytesRead, bufferBytes, 0, (int)(buffer.Length - bytesRead));
        buffer = bufferBytes.AsBuffer();
    }
    else
    {
        byte[] bufferBytes = new byte[1024];
        buffer = bufferBytes.AsBuffer();
    }
}

我做错了什么?

为完成这项工作而修复的问题:

  • 在解析数据包循环之外获取重要变量,因为我们下次需要解析另一个数据包时需要该值。
  • 如果收到不完整的应用程序或游戏数据包,请读取数据包 header 一次。
  • 只加载我们需要的字节
  • 使用 UnconsumedBufferLength.
  • 完全接收后,读取 header 和有效负载

代码:

short command = 0;
short payloadSize = 0;
byte[] payload = null;

bool packetHeaderRead = false;

while (connected)
{
    if (!packetHeaderRead)
    {
        if (dataReader.UnconsumedBufferLength < PacketHeaderSize)
        {
            int headerBytesLeft = PacketHeaderSize - (int)dataReader.UnconsumedBufferLength;

            if (headerBytesLeft > 0)
            {
                await dataReader.LoadAsync((uint)headerBytesLeft);
                continue;
            }
        }
        else
        {
            command = dataReader.ReadInt16();
            payloadSize = dataReader.ReadInt16();
            packetHeaderRead = true;
            continue;
        }
    }
    else
    {
        int payloadBytesLeft = payloadSize - (int)dataReader.UnconsumedBufferLength;

        if (payloadBytesLeft > 0)
        {
            await dataReader.LoadAsync((uint)payloadBytesLeft);
        }

        if (payloadSize == 0)
        {
            Packet packet = new Packet(command, payloadSize, payload);
            ParsePacket(packet);

            packetHeaderRead = false;
        }
        else if (dataReader.UnconsumedBufferLength >= payloadSize)
        {
            payload = new byte[payloadSize];
            dataReader.ReadBytes(payload);

            Packet packet = new Packet(command, payloadSize, payload);
            ParsePacket(packet);

            packetHeaderRead = false;
        }
    }
}