Frage

Ich habe laufen in dem, was ich glaube, ist ein Problem mit den BinaryReader.ReadChars () -Methode. Wenn ich eine Binary um eine Raw-Socket Network wickeln gelegentlich erhalte ich einen Strom von Korruption, wo der Strom nicht synchron wird gelesen. Der Strom in Frage enthält Nachrichten in einem binären Serialisierung Protokoll.

Ich habe diese aufgespürt die folgende

  • Es geschieht nur, wenn eine Unicode-Zeichenfolge (codiert mit dem Encoding.BigEndian) Lesen
  • Es geschieht nur, wenn die Zeichenfolge in Frage über zwei TCP-Pakete aufgeteilt werden (bestätigt mit wireshark)

Ich denke, was passiert, ist die folgende (in Zusammenhang mit dem Beispiel unten)

  • BinaryReader.ReadChars () aufgerufen wird es gefragt 3 Zeichen zu lesen (Stringlängen codiert werden vor dem eigentlichen String)
  • Erste Schleife intern fordert ein Lesen von 6 Bytes (3 verbleibenden Zeichen * 2 Bytes / char) aus dem Netzwerkstrom
  • Netzstrom hat nur 3 Bytes verfügbar
  • 3 Bytes in den lokalen Puffer lesen
  • Buffer geben Decoder
  • Decoder decodiert 1 Zeichen, und hält das andere Byte in einem eigenen internen Puffer
  • Zweite Schleife intern fordert eine Lese von 4 Bytes! (2 verbleibenden Zeichen * 2 Byte / Zeichen)
  • Netzstrom hat alle 4 Bytes verfügbar
  • 4 Bytes in den lokalen Puffer lesen
  • Buffer geben Decoder
  • Decoder decodiert 2 char, und hält die restlichen 4 Bytes intern
  • String decode abgeschlossen
  • Serialisierung Code versucht, das nächste Element zu entpacken und krächzt wegen Strom Korruption.

    char[] buffer = new char[3];
    int charIndex = 0;
    
    Decoder decoder = Encoding.BigEndianUnicode.GetDecoder();
    
    // pretend 3 of the 6 bytes arrives in one packet
    byte[] b1 = new byte[] { 0, 83, 0 };
    int charsRead = decoder.GetChars(b1, 0, 3, buffer, charIndex);
    charIndex += charsRead;
    
    // pretend the remaining 3 bytes plus a final byte, for something unrelated,
    // arrive next
    byte[] b2 = new byte[] { 71, 0, 114, 3 };
    charsRead = decoder.GetChars(b2, 0, 4, buffer, charIndex);
    charIndex += charsRead;
    

Ich denke, die Wurzel ein Fehler in dem .NET-Code ist der charsRemaining * Bytes verwendet / jede Schleife verkohlt das verbleibenden Bytes erforderlich zu berechnen. Aufgrund des zusätzlichen Byte in dem Decoder versteckt kann diese Berechnung aus einem zusätzlichen Byte für seine wodurch den Eingangsstrom verbraucht ausgeschaltet werden.

Hier ist der .NET Framework-Code in Frage

    while (charsRemaining>0) { 
        // We really want to know what the minimum number of bytes per char 
        // is for our encoding.  Otherwise for UnicodeEncoding we'd have to
        // do ~1+log(n) reads to read n characters. 
        numBytes = charsRemaining;
        if (m_2BytesPerChar)
            numBytes <<= 1;

        numBytes = m_stream.Read(m_charBytes, 0, numBytes);
        if (numBytes==0) { 
            return (count - charsRemaining); 
        } 
        charsRead = m_decoder.GetChars(m_charBytes, 0, numBytes, buffer, index);

        charsRemaining -= charsRead;
        index+=charsRead;
    }

Ich bin mir nicht ganz sicher, ob dies ein Fehler ist oder nur ein Missbrauch des API. Arbeiten rund diesem Thema bin ich der Berechnung nur die Bytes selbst erforderlich, sie zu lesen, und dann läuft die byte [] durch die entsprechenden Encoding.GetString (). Dies würde jedoch nicht für so etwas wie UTF-8 arbeiten.

Seien Sie interessiert die Gedanken der Menschen auf das zu hören, und ob ich etwas falsch oder nicht tun. Und vielleicht wird es die nächste Person ein paar Stunden / Tage der langwierigen Fehlersuche speichern.

EDIT: posted verbinden Connect-Tracking-Element

War es hilfreich?

Lösung

I wiedergegeben haben das Problem, das Sie mit BinaryReader.ReadChars erwähnt.

Obwohl die Entwickler immer benötigen für Look-Ahead-Rechnung zu tragen, wenn die Dinge wie Ströme und Decoder Komponieren, scheint dies wie ein ziemlich bedeutenden Fehler in BinaryReader weil diese Klasse zum Lesen von Datenstrukturen aus verschiedenen Arten von Daten vorgesehen ist. In diesem Fall stimme ich, dass ReadChars in konservativer gewesen sein sollte, was es zu verlieren, dass die Byte zu vermeiden lesen.

Es ist nichts falsch mit Ihrem Problem zu umgehen, die Decoder direkt verwenden, nachdem alles, was was ReadChars hinter den Kulissen tut.

Unicode ist ein einfacher Fall. Wenn Sie über eine beliebige Codierung denken, gibt es wirklich keinen Allzweck Weg, um sicherzustellen, dass die richtige Anzahl von Bytes verbraucht werden, wenn Sie in einer Zeichenzahl anstelle einer Byteanzahl (man denke etwa unterschiedliche Länge Zeichen und Fälle, in denen fehlerhafte Eingabe) übergeben. Aus diesem Grunde BinaryReader.ReadChars für die bestimmte Anzahl von Bytes zu lesen bietet eine robustere, allgemeine Lösung zu vermeiden.

Ich würde vorschlagen, dass Sie diese Aufmerksamkeit zu Microsoft bringen über http://connect.microsoft.com/visualstudio .

Andere Tipps

Interessante; Sie könnte dies berichten über „connect“. Als Notlösung, können Sie auch versuchen, Verpackung mit BufferredStream , aber ich erwarte, dass dies einen Riss tapeziert über (es kann immer noch passieren, aber weniger häufig).

Der andere Ansatz, natürlich, ist eine gesamte Nachricht Vorpuffers (aber nicht der gesamte Strom); dann von so etwas wie MemoryStream lesen - Ihr Netzwerkprotokoll unter der Annahme, hat logische (und idealerweise längenvoran, und nicht zu groß) Nachrichten. Dann, wenn es Dekodierung alle Daten verfügbar sind.

Das erinnert an einen meiner eigenen Fragen ( Lesung aus einem HttpResponseStream nicht ) wo ich hatte ein Problem, dass, wenn von einem HTTP-Antwort-Stream der Stream lesen würde denken, dass es das Ende des Stroms vorzeitig getroffen hatte so meine Parser unerwartet bombardieren würden.

Wie Marc für Ihr Problem vorgeschlagen, dass ich versuchte, erstes Pre-Buffering in einem MemoryStream der gut funktioniert, aber heißt, Sie müssen lange warten, wenn Sie eine große Datei zu lesen (vor allem aus dem Netzwerk / Internet), bevor Sie tun etwas Sinnvolles mit ihm. Ich entschied mich schließlich auf meine eigene Erweiterung von Textreader zu schaffen, die die Read Methoden überschreibt und definiert sie die Lies-Block-Methode (das ist eine Sperr lesen heißt, es wartet, bis er genau die Anzahl der Zeichen bekommen können Sie fragen Sie nach)

Ihr Problem wahrscheinlich auf die Tatsache zurückzuführen, wie das meine ist, dass Read Methoden nicht sind guarenteed die Anzahl der Zeichen zurückkehren Sie bitten, wenn Sie zum Beispiel in der Dokumentation für die BinaryReader.Read suchen ( http://msdn.microsoft.com/en-us/library/ms143295.aspx ) Methode, die Sie siehe ‚ll, dass es heißt:

  

Rückgabewert
  Typ: System .. ::. Int32
  Die Anzahl der gelesenen Zeichen in den Puffer. Dies könnte kleiner sein als die Anzahl der angeforderten Bytes, wenn dass viele Bytes nicht verfügbar sind, oder es könnte Null sein, wenn das Ende des Stroms erreicht wird.

Da Binary hat keine Lies-Block-Methoden wie ein Textreader alles, was Sie tun können, ist Ihren eigenen Ansatz zur Überwachung der Position selbst oder Marcs von Pre-Caching nehmen.

Ich arbeite mit Unity3D / Mono atm und die ReadChars-Methode könnte noch Fehler enthalten. Ich habe eine Zeichenfolge wie folgt:

mat.name = new string(binaryReader.ReadChars(64));

mat.name enthielt auch die richtige Zeichenfolge, aber ich konnte einfach hinzufügen Strings vor es. Alles nach dem String nur disappered. Auch bei String.Format. Meine Lösung so weit ist die ReadChars-Methode nicht verwenden, aber die Daten als Byte-Array lesen und wandelt es in einen String:

byte[] str = binaryReader.ReadBytes(64);
int lengthOfStr = Array.IndexOf(str, (byte)0); // e.g. 4 for "clip\0"
mat.name = System.Text.ASCIIEncoding.Default.GetString(str, 0, lengthOfStr);
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top