【问题标题】:Parsing variable-sized packets解析可变大小的数据包
【发布时间】:2017-07-08 21:40:42
【问题描述】:

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

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

2 字节短命令,2 字节短有效载荷大小,(可选)有效载荷字节

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();
    }
}

我做错了什么?

【问题讨论】:

  • 在编写 uwp 应用程序时,请确保您在问题上有 uwp 标签,uwp 应用程序框架中可用的类与桌面应用程序不同。
  • @ScottChamberlain 我的错,谢谢。
  • 如果没有可靠地重现问题的良好 minimal reproducible example 就不可能确定,但​​您的代码似乎有几乎任何以前不费心研究网络协议的人都会犯的标准错误深入研究网络代码:您期望同时接收所有数据。 TCP 不是这样工作的。套接字是一个字节流,数据一次可以少至一个字节(尽管实际上它通常不是那么糟糕:))。
  • 在任何给定的字节消耗状态下,您都需要继续阅读,直到获得所需的所有字节。鉴于您发布的代码,ParsePacket() 根本不被称为 有点奇怪,但预期它可能会被不完整的数据调用当然是合理的,因为您没有做任何事情来确保您'已经读取数据,直到你得到预期的那样多。还有一个问题是,您可以将下一条消息的标头作为当前消息的一部分读取,从而破坏数据流。
  • @PeterDuniho 我知道混合数据包或不完整的数据,我主要担心的是我的代码过于复杂,但我已经更新了我的问题的代码。不过,我对如何将当前的字节流附加到缓冲区非常迷茫。谢谢。

标签: c# uwp network-protocols


【解决方案1】:

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

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

代码:

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;
        }
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-08-16
    • 1970-01-01
    • 2016-01-08
    • 1970-01-01
    • 1970-01-01
    • 2010-12-01
    • 2019-09-19
    • 2021-03-10
    相关资源
    最近更新 更多