2009-10-08 5 views
0

Je rencontre un problème dans une application multithread et je l'ai débogué pendant les 3 derniers jours, mais pour la vie de celui-ci ne peut pas le comprendre. J'écris ceci, en espérant que j'ai soit un moment DUH en tapant ceci ou quelqu'un voit quelque chose d'évident dans les extraits de code que je fournis. Voilà ce qui se passe:Les datagrammes de décodage C# UDP échouent de manière aléatoire

Je travaille sur une nouvelle bibliothèque de réseau UDP et ont un producteur de données multidestinataire datagrammes UDP à plusieurs applications de récepteur. L'expéditeur envoie deux sockets différents qui sont liés à séparer les adresses de multidiffusion UDP et les ports distincts. L'application de réception crée également deux sockets et les relie à l'adresse/port multidiffusion de l'expéditeur. Lorsque le récepteur reçoit le datagramme, il le copie du buffer dans un MemoryStream qui est ensuite placé dans une file d'attente thread-thread, où un autre thread le lit et décode les données hors du MemoryStream.

Les deux sockets ont leurs propres files d'attente. Ce qui se passe maintenant est vraiment bizarre, ça arrive de manière aléatoire, non reproductible et quand j'exécute plusieurs applications de récepteurs, ça n'arrive que de temps en temps sur l'un d'eux de temps en temps.

Fondamentalement, le thread qui lit le MemoryStream hors de la file d'attente, le lit via un BinaryReader tel que ReadInt32(), etc. et décode ainsi les données. De temps en temps, cependant, lorsqu'il lit les données, les données qu'il lit sont incorrectes, par ex. un nombre entier négatif que l'expéditeur ne coderait jamais.

Cependant, comme mentionné précédemment, le décodage échoue seulement dans l'une des applications du récepteur, dans les autres le datagramme décode bien. Maintenant, vous pourriez dire, bien, le datagramme UDP a probablement une corruption d'octets ou quelque chose mais j'ai connecté chaque datagramme qui arrive et les compare sur tous les récepteurs et les datagrammes que chaque application reçoit sont absolument identiques. Maintenant, il devient encore plus bizarre, quand je jette le datagramme qui a échoué à décoder sur le disque et écrire un test unitaire qui le lit et l'exécute dans le décodeur, il décode très bien. Aussi, lorsque j'emballe un try/catch autour du décodeur, réinitialise la position MemoryStream dans le catch et le passe à nouveau dans le décodeur, il décode très bien. Pour le rendre encore plus bizarre, cela ne se produit que lorsque je lie les deux sockets pour lire les données de l'expéditeur, si je ne fais que lier un, cela n'arrive pas ou au moins je ne pouvais pas le reproduire.

Voici est un code correspondant à ce qui se passe:

Ceci est la réception de rappel pour la prise:

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

Voici un code stub qui décode les données:

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

Vous voyez dans la partie catch {} je réinitialise simplement MemoryStream.Position à 0 et j'appelle la même méthode encore une fois et ça marche très bien la prochaine fois? Je suis vraiment à court d'idées à ce stade et malheureusement, il n'y a pas eu de moment où DUH a écrit ça. Quelqu'un at-il une idée de ce qui pourrait se passer ou de ce que je pourrais faire d'autre pour résoudre ce problème?

Merci,

Tom

+0

Cela n'arrive que de façon aléatoire, et quand cela arrive, vous remédiez toujours à la situation en récursif dans la clause de catch? – csl

+0

correct, jusqu'à présent, il n'a jamais échoué une deuxième fois après que j'appelle à nouveau OnDataChunk dans le bloc catch – TJF

Répondre

0

Il semble que la corruption de données se produit en raison d'un accès non sync par plusieurs threads. Pouvez-vous confirmer que les membres datachunk sont accessibles en mode thread sécurisé?

Questions connexes