C#UDP解码数据报随机失败

时间:2009-10-08 18:52:01

标签: c# sockets udp multicast memorystream

我在多线程应用程序中遇到问题,并且在过去3天内一直在调试它,但是它的使用寿命无法解决。我正在写这篇文章,希望我在输入时有一个DUH时刻,或者有人在我提供的代码片段中看到了一些明显的东西。 这是正在发生的事情:

我一直致力于新的UDP网络库,并且拥有一个数据生成器,可以将UDP数据报多播到多个接收器应用程序。发送方发送两个不同的套接字,这些套接字绑定到单独的UDP多播地址和单独的端口。 接收器应用程序还创建两个套接字,并将每个套接字绑定到发送方的多播地址/端口之一。

当接收方收到数据报时,它会从MemoryStream中的缓冲区中复制它,然后将其放入线程安全队列,其中另一个线程从中读取并从MemoryStream中解码数据。

两个套接字都有自己的队列。

现在发生的事情真的很奇怪,它是随机发生的,不可重现的,当我运行多个接收器应用程序时,它只会偶尔在其中一个上随机发生。

基本上,从队列中读取MemoryStream的线程通过像ReadInt32()等BinaryReader读取它,从而解码数据。然而,当它读取数据时,它从中读取的数据是不正确的,例如,发送者永远不会编码的负整数。

然而,如前所述,解码仅在一个接收器应用程序中失败,而在其他接收器应用程序中,数据报解码很好。 现在你可能会说,好吧,UDP数据报可能有字节损坏或者其他什么,但是我记录了每一个数据报,并在所有接收器上对它们进行比较,每个应用程序收到的数据报都完全相同。 现在它变得更加怪异,当我将无法解码的数据报转储到磁盘并编写一个读取它并通过解码器运行的单元测试时,它解码得很好。此外,当我在解码器周围包装try / catch时,重置catch中的MemoryStream位置并再次通过解码器运行它,它解码得很好。 为了使它更奇怪,这也只发生在我绑定两个套接字以从发送方读取数据时,如果我只绑定一个,它不会发生或者至少我无法重现它。

以下是一些与之相关的代码:

这是套接字的接收回调:

        private void ReceiveCompleted(object sender, SocketAsyncEventArgs args)
    {
        if (args.SocketError != SocketError.Success)
        {
            InternalShutdown(args.SocketError);
            return;
        }

        if (args.BytesTransferred > SequencedUnitHeader.UNIT_HEADER_SIZE)
        {
            DataChunk chunk = new DataChunk(args.BytesTransferred);
            Buffer.BlockCopy(args.Buffer, 0, chunk.Buffer, 0, args.BytesTransferred);

            chunk.MemoryStream = new MemoryStream(chunk.Buffer);
            chunk.BinaryReader = new BinaryReader(chunk.MemoryStream);

            chunk.SequencedUnitHeader.SequenceID = chunk.BinaryReader.ReadUInt32();
            chunk.SequencedUnitHeader.Count = chunk.BinaryReader.ReadByte();

            if (prevSequenceID + 1 != chunk.SequencedUnitHeader.SequenceID)
            {
                log.Error("UdpDatagramGap\tName:{0}\tExpected:{1}\tReceived:{2}", unitName, prevSequenceID + 1, chunk.SequencedUnitHeader.SequenceID);
            }
            else if (chunk.SequencedUnitHeader.SequenceID < prevSequenceID)
            {
                log.Error("UdpOutOfSequence\tName:{0}\tExpected:{1}\tReceived:{2}", unitName, prevSequenceID + 1, chunk.SequencedUnitHeader.SequenceID);
            }

            prevSequenceID = chunk.SequencedUnitHeader.SequenceID;

            messagePump.Produce(chunk);
        }
        else
            UdpStatistics.FramesRxDiscarded++;

        Socket.InvokeAsyncMethod(Socket.ReceiveAsync, ReceiveCompleted, asyncReceiveArgs);
    }

这是一些解码数据的存根代码:

        public static void OnDataChunk(DataChunk dataChunk)
    {
        try
        {
            for (int i = 0; i < dataChunk.SequencedUnitHeader.Count; i++)
            {
                int val = dataChunk.BinaryReader.ReadInt32();

                if(val < 0)
                    throw new Exception("EncodingException");

                // do something with that value
            }
        }
        catch (Exception ex)
        {
            writer.WriteLine("ID:" + dataChunk.SequencedUnitHeader.SequenceID + " Count:" + dataChunk.SequencedUnitHeader.Count + " " + BitConverter.ToString(dataChunk.Buffer, 0, dataChunk.Size));
            writer.Flush();
            log.ErrorException("OnDataChunk", ex);

            log.Info("RETRY FRAME:{0} Data:{1}", dataChunk.SequencedUnitHeader.SequenceID, BitConverter.ToString(dataChunk.Buffer, 0, dataChunk.Size));
            dataChunk.MemoryStream.Position = 0;

            dataChunk.SequencedUnitHeader.SequenceID = dataChunk.BinaryReader.ReadUInt32();
            dataChunk.SequencedUnitHeader.Count = dataChunk.BinaryReader.ReadByte();

            OnDataChunk(dataChunk);
        }
    }

你在catch {}部分看到我只是将MemoryStream.Position重置为0并再次调用相同的方法,它下次工作正常吗? 我现在真的没有想法,很遗憾没有DUH时刻写这篇文章。 任何人都有任何想法可能会发生什么或我还能做些什么来解决这个问题?

谢谢,

汤姆

1 个答案:

答案 0 :(得分:0)

由于多个线程的不同步访问,似乎发生了数据损坏。你能否确认以线程安全的方式访问datachunk成员?