C # UDP декодирование дейтаграмм происходит случайно - PullRequest
0 голосов
/ 08 октября 2009

У меня возникла проблема в многопоточном приложении, и я отлаживал его последние 3 дня, но по жизни он не может понять это. Я пишу это, надеясь, что у меня либо будет ОЧЕНЬ момент, когда я наберу это, либо кто-то увидит что-то очевидное в предоставленных мной фрагментах кода. Вот что происходит:

Я работал над новой сетевой библиотекой UDP, и у меня есть производитель данных, который многоадресно передает дейтаграммы UDP нескольким приложениям-получателям. Отправитель отправляет по двум разным сокетам, которые привязаны к отдельным многоадресным адресам UDP и отдельным портам. Приложение-получатель также создает два сокета и связывает каждый из них с одним из многоадресного адреса / порта отправителя.

Когда получатель получает дейтаграмму, он копирует ее из буфера в MemoryStream, который затем помещается в потокобезопасную очередь, где другой поток читает из нее и декодирует данные из MemoryStream.

Оба сокета имеют свои очереди.

То, что происходит сейчас, действительно странно, это происходит случайно, невоспроизводимо, и когда я запускаю несколько приложений-получателей, это происходит случайным образом только на одном из них время от времени.

По сути, поток, который считывает MemoryStream из очереди, читает его через BinaryReader, такой как ReadInt32 () и т. Д., И таким образом декодирует данные. Время от времени, однако, когда он читает данные, данные, которые он читает, неверны, например, отрицательное целое число, которое отправитель никогда не закодирует.

Однако, как упоминалось ранее, декодирование завершается сбоем только в одном из приложений приемника, в других дейтаграмма декодируется нормально. Теперь вы можете сказать, что, возможно, датаграмма UDP имеет повреждение байтов или что-то в этом роде, но я зарегистрировал каждую входящую датаграмму и сравнил их на всех приемниках, а датаграммы, которые получает каждое приложение, абсолютно идентичны. Теперь это становится еще более странным: когда я сбрасываю дейтаграмму, которая не удалось декодировать, на диск и записываю модульный тест, который читает его и запускает через декодер, он прекрасно декодируется. Также, когда я обертываю попытку / ловушку вокруг декодера, сбрасываю позицию 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 и снова вызываю тот же метод, и он в следующий раз работает нормально? У меня действительно нет идей на данный момент, и, к сожалению, у меня не было времени, чтобы написать это. У кого-нибудь есть какие-либо идеи о том, что может происходить или что еще я мог бы сделать, чтобы устранить это?

Спасибо

Tom

1 Ответ

0 голосов
/ 15 июня 2010

Похоже, что повреждение данных происходит из-за несинхронного доступа несколькими потоками. Можете ли вы подтвердить, что доступ к членам datachunk осуществляется потокобезопасным способом?

...