2014-01-20 4 views
5

Sagen wir, ich möchte nicht blockierende Lesevorgänge von einem Netzwerk-Socket tun. Ich kann async warten auf den Socket zu lesen x Bytes und alles ist in Ordnung.Wie macht man nicht blockiert Socket liest mit Protobuf mit C#?

Aber wie kombiniere ich das mit Deserialisierung über Protobuf?

Lesen von Objekten aus einem Stream muss blockieren? Das heißt, wenn der Stream zu wenig Daten für den Parser enthält, muss hinter den Kulissen blockiert werden, damit der Leser alle benötigten Bytes abrufen kann.

Ich denke, ich kann Länge Prefix Trennzeichen verwenden und die ersten Bytes lesen und dann herausfinden, wie viele Bytes ich Minimum holen muss, bevor ich analysieren, ist das der richtige Weg? z.B. Wenn mein Puffer 500 Bytes ist, dann erwarte diese 500 Bytes, und analysiere die Länge Präfix, und wenn die Länge mehr als 500 ist, dann warte wieder, bis alles gelesen ist.

Was ist die idiomatische Möglichkeit, nicht blockierende IO- und Protobuf-Parsing zu kombinieren?

(Ich bin mit Jon Skeet Implementierung jetzt http://code.google.com/p/protobuf-csharp-port/)

Antwort

6

Als allgemeine Regel gilt, Serializer nicht enthalten oft eine DeserializeAsync Methode, denn das ist wirklich wirklich hart (zumindest zu tun, effizient). Wenn die Daten von mittlerer Größe sind, würde ich empfehlen, die erforderliche Datenmenge mit asynchronem Code zu puffern - und dann zu deserialisieren, wenn alle erforderlichen Daten verfügbar sind. Wenn die Daten sehr groß sind und Sie nicht alles im Speicher zwischenspeichern möchten, sollten Sie eine regelmäßige synchrone Deserialisierung für einen Arbeitsthread verwenden.

(beachten Sie, dass dies zur Kenntnis Implementierung spezifisch ist, aber wenn der Serializer Sie verwenden tut Unterstützung ein asynchrones deserialize: dann sicher, verwenden Sie das)

+0

Können Sie mehr darüber erfahren, was so schwer daran ist? Ersetzen Sie nicht jedes 'Read()' mit 'award ReadAsync()' genug? – svick

+2

@svick zuerst, bemerkte ich "effizient". Deserialisierung ist oft eine Vielzahl von einzelnen einfachen Operationen. Es ist unangemessen, für jede eine Aufgabe zu erstellen. Zweitens: Viele Serialisierer verwenden Meta-Programmierung, was bedeutet, dass der generierte Code dies auch implementieren muss - und beachte, dass Meta-Programmierung nicht immer "erzeugt C# bedeutet, das dem Compiler zugeführt wird". Wenn Sie alles tun müssten, was "erwarten" würde, manuell, in IL - könnten Sie einfach verrückt werden. Crazier, in meinem Fall. –

0

Verwenden Sie die BeginReceive/EndRecieve() Methoden, um Ihre Daten in ein Byte zu empfangen Puffer (normalerweise 1024 oder 2048 Bytes). In der AsyncCallback, nachdem sichergestellt, dass Sie nicht -1,0 Bytes (Ende des Streams/Trennen/IO-Fehler) gelesen haben, versuchen, das Paket mit ProtocolBuf deserialisieren.

Ihr Receive-Callback wird asynchron sein, und es ist sinnvoll, das Paket im selben Thread zu analysieren wie das Lesen, IMHO. Es ist die Handhabung, die wahrscheinlich den größten Leistungseinbruch verursacht.

+0

Nun, wenn die Nachricht ist> buffer.Length, dann wird es fehlschlagen und ich habe CPU-Zyklen bei der Analyse unvollständiger Daten verschwendet .. das muss mit Länge Präfixe behandelt werden Ich denke (?) –

+0

Warum würden Sie eine Nachricht größer senden als 1024/2048 Bytes? Abgesehen von Dateiübertragungen natürlich. Implementierungen, die ich dabei gemacht habe, würden die Daten in 1024/2048 Byte-Blöcken senden und sie durch eine Signaling-Ganzzahl gruppieren, um ihre Reihenfolge zu signalisieren; der Server würde sie dann auf der Serverseite erneut verbinden. –

+0

Jede Nachricht, die ein Textfeld enthält, könnte 1024 Byte leicht beschädigen. Es scheint ein schwacher Ansatz zu sein, um diese Art von Begrenzung in das System einzubauen. –