4 Stimmen

Effiziente Aufteilung der in einem Datenstrom eingehenden, durch Begrenzungslinien getrennten Nachrichten

Ich möchte einen speichereffizienten und zeitsparenden Weg finden, um einen eingehenden Datenstrom auf der Grundlage eines Delimeters aufzuteilen. Der Strom ist ein Netzwerkstrom und die eingehenden "Nachrichten" werden aufgeteilt durch CRLF . Bisher habe ich dies getan, indem ich eingehende Daten in eine Zeichenkette mit UTF8 konvertiert habe, dann auf CRLF geprüft habe und wenn es vorhanden ist, habe ich auf dieser Grundlage geteilt, aber das ist kein sehr guter Weg, um das Problem zu lösen, da mehr und mehr Nachrichten eingehen. Außerdem kann es sein, dass ich Datenpakete erhalte, die 1 Nachricht enthalten, und ich kann Datenpakete erhalten, die 10 Nachrichten enthalten, und sogar welche, die nur Teile von Nachrichten enthalten.

Das habe ich mir bis jetzt ausgedacht. Ich verwende einen Memorystream als Puffer, und wenn Daten eingehen, lese ich die Daten in den Memorystream. Wenn ich das Delimeter (CRLF) finde, nehme ich alle Daten im memorystream, und rufen Sie die messageReceived auf, dass, dann ich weiter. Hat jemand eine Idee dazu?

[Bearbeiten]
Ok, ich glaube, ich muss besser erklären, was ich tun will. Das verwendete Protokoll ist das IRC-Protokoll, das "Nachrichten" oder "Befehle" sendet, wenn man will, dargestellt durch CRLF . Ich verwende die Socket-Klasse in C# mit BeginReceive und EndReceive, also läuft alles asynchron. Die Klasse, die ich schreibe, heißt MessageConnection. Sie empfängt Daten von einem tcp-Socket, und wenn ein bestimmtes Delimeter gefunden wird (in diesem Fall CRLF ) Ich möchte, dass es eine Funktion namens OnMessage aufruft, die die empfangene Nachricht als Parameter erhält. Ich habe genau das gleiche Problem gelöst, bevor mit einem StringBuilder als Puffer, und die neue Zeichenfolge an den StringBuilder anhängen, wann immer ich Daten empfangen, dann würde ich die Zeichenfolge durch den StringBuilder basierend auf dem Delimeter zurückgegeben, leeren Sie den StringBuilder, und fügen Sie den letzten Teil der Split-Operation. Danach führe ich eine Schleife durch das Split-Array (ohne das letzte Element) und rufe OnMessage auf. Dies howerver fühlt sich wie eine ineffiziente Art und Weise der Lösung des Problems, weil ich eine Menge von Konvertierung zu und von Zeichenfolgen - die nicht verry gut sein soll, so dass ich dachte, es muss eine einfache Möglichkeit, dies zu lösen, ohne in Zeichenfolgen zu denken, nur in Byte-Arrays, und nur in eine Zeichenfolge konvertieren, wenn ich ein Byte-Array, die eine actuall "Nachricht" darstellt, und das ist, was ich Hilfe mit wollen.

1voto

Mike Christiansen Punkte 1050

Ich denke, Sie haben die richtige Idee. Tun Sie es einfach mit einem Byte-Array.

So würde ich es machen, rein ungetestet, und könnte optimiert werden....

byte[] m_LongBuffer;
byte[] m_SmallBuffer;
void ReceiveCallback(IAsyncResult iar)
{
   //m_SmallBuffer contains the data read from the stream
   //Append it to m_LongBuffer
   int bytesread = socket.EndReceive(iar);
   m_LongBuffer = m_LongBuffer.Concat(m_SmallBuffer.Take(bytesread)).ToArray();

   int startpoint = 0;
   int splitpoint = 0;
   int lastendpoint = 0;
   bool twochar = false;

   do
   {
       int i = 0;
       for(i = 0;i < m_LongBuffer.Length; ++i)
       {
           if((m_LongBuffer[i] == 0x0A) || (m_LongBuffer[i] == 0x0D))
           {
               splitpoint = i;
               if((m_LongBuffer[i+1] == 0x0A) || (m_LongBuffer[i+1] == 0x0D))
                    twochar=true;
               else
                    twochar=false;

               lastendpoint = splitpoint;                   
               String message = ASCII.ASCIIEncoding.GetString(m_LongBuffer.Skip(startpoint).Take(splitpoint - startpoint).ToArray());
               //Do something with the message
               startpoint = splitpoint + (twochar ? 2 : 1);
               break;
           }
       }
       if(i >= m_LongBuffer.Length)
            splitpoint = -1;
   } while (splitpoint != -1);
   m_LongBuffer = m_LongBuffer.Skip(lastendpoint).ToArray();
}

0voto

Jim Mischel Punkte 125706

Vor einiger Zeit musste ich etwas Ähnliches tun. Ich habe es gelöst, indem ich einen Producer/Consumer-Stream erstellt habe. Der Produzent (in Ihrem Fall die Sache, die den Netzwerkstrom liest) schreibt Bytes in den Strom, und der Konsument erstellt eine StreamReader mit dem Strom verbunden.

Zugegeben, dies erfordert einen weiteren Thread für den Verbraucher, aber es verhindert Probleme, die auftreten könnten, wenn der Rückruf zu lange dauert und Sie am Ende Nachrichten verpassen.

Ich schrieb den Stream auf, den ich als ProducerConsumerStream in einem Artikel. Siehe unter http://www.informit.com/guides/content.aspx?g=dotnet&seqNum=852 .

Eine frühere Lösung des Problems bestand darin, das Byte-Array selbst zu parsen. Das funktionierte, war aber nicht so flexibel wie dieser Stream-Ansatz.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X