2009-08-12 14 views
1

Hey, ich habe ein Problem, Pakete mit einem benutzerdefinierten Binärprotokoll zu trennen. Derzeit sieht der Server-Code so aus.TCP-Framing mit binärem Protokoll

public void HandleConnection(object state) 
    { 
     TcpClient client = threadListener.AcceptTcpClient(); 
     NetworkStream stream = client.GetStream(); 
     byte[] data = new byte[4096]; 

     while (true) 
     { 
      int recvCount = stream.Read(data, 0, data.Length); 
      if (recvCount == 0) break; 
      LogManager.Debug(Utility.ToHexDump(data, 0, recvCount)); 
      //processPacket(new MemoryStream(data, 0, recvCount)); 
     } 
     LogManager.Debug("Client disconnected"); 
     client.Close(); 
     Dispose(); 
    } 

Ich habe den Hex-Dumps der Pakete zu beobachten, und manchmal das ganze Paket kommt in einem Schuss, lassen Sie sich alle 20 Bytes sagen. Andere Zeiten, in denen es fragmentiert ist, wie muss ich diese Daten puffern, um sie korrekt an meine processPacket() -Methode übergeben zu können. Ich versuche, nur einen einzelnen Byte-Opcode-Header zu verwenden, sollte ich etwas wie eine (ushort) contentLength auch zum Header hinzufügen? Ich versuche, das Protokoll so leicht wie möglich zu machen, und dieses System wird nicht sehr große Pakete senden (< 128 Bytes).

Der clientseitige Code, den ich testen möchte, ist wie folgt.

public void auth(string user, string password) 
    { 
     using (TcpClient client = new TcpClient()) 
     { 
      client.Connect(IPAddress.Parse("127.0.0.1"), 9032); 
      NetworkStream networkStream = client.GetStream(); 

      using (BinaryWriter writer = new BinaryWriter(networkStream)) 
      { 
       writer.Write((byte)0); //opcode 
       writer.Write(user.ToUpper()); 
       writer.Write(password.ToUpper()); 
       writer.Write(SanitizationMgr.Verify()); //App hash 
       writer.Write(Program.Seed); 
      } 
     } 
    } 

Ich bin mir nicht sicher, ob das sein könnte, was es vermasselt, und Binärprotokolls scheint nicht viele Informationen im Internet, insbesondere bei der C# beteiligt ist. Jeder Kommentar wäre hilfreich. =)

Gelöst mit diesem, nicht sicher, ob es korrekt ist, aber es scheint meinen Handlern genau das zu geben, was sie brauchen.

public void HandleConnection(object state) 
    { 
     TcpClient client = threadListener.AcceptTcpClient(); 
     NetworkStream stream = client.GetStream(); 
     byte[] data = new byte[1024]; 

     uint contentLength = 0; 
     var packet = new MemoryStream(); 
     while (true) 
     { 
      int recvCount = stream.Read(data, 0, data.Length); 
      if (recvCount == 0) break; 

      if (contentLength == 0 && recvCount < headerSize) 
      { 
       LogManager.Error("Got incomplete header!"); 
       Dispose(); 
      } 

      if(contentLength == 0) //Get the payload length 
       contentLength = BitConverter.ToUInt16(data, 1); 

      packet.Write(data, (int) packet.Position, recvCount); //Buffer the data we got into our MemStream 
      if (packet.Length < contentLength + headerSize) //if it's not enough, continue trying to read 
       continue; 

      //We have a full packet, pass it on 
      //LogManager.Debug(Utility.ToHexDump(packet)); 
      processPacket(packet); 

      //reset for next packet 
      contentLength = 0; 
      packet = new MemoryStream(); 
     } 
     LogManager.Debug("Client disconnected"); 
     client.Close(); 
     Dispose(); 
    } 

Antwort

3

Sie sollten es nur als Stream behandeln. Verlassen Sie sich nicht auf ein bestimmtes Chunking-Verhalten.

Ist die Menge der Daten immer gleich? Wenn nicht, sollten Sie das Protokoll ändern (wenn Sie können), um den logischen "Chunk" von Daten mit der Länge in Bytes voranzuzählen.

In diesem Fall Sie BinaryWriter auf der einen Seite mit bist, so ein BinaryReader zum NetworkStream zurück durch TcpClient.GetStream() Befestigung wie der einfachste Ansatz scheinen würde. Wenn Sie jedoch wirklich alle Daten für einen Chunk gleichzeitig erfassen möchten, sollten Sie zu meiner Idee zurückkehren, die Daten mit ihrer Länge voranzutreiben. Dann drehen Sie einfach um, bis Sie alle Daten erhalten haben.

(Stellen Sie sicher, dass Sie genügend Daten habe, obwohl die Länge zu lesen! Wenn Ihr Längenpräfix 4 Bytes ist, die Sie nicht wollen 2 Bytes lesen und die nächsten 2 ... verpassen)

+0

Hmm, Ich werde der Kopfzeile eine contentLength hinzufügen, danke. Ich benutze einen binären Leser in processPacket(), aber offensichtlich scheitert es schrecklich, wenn es nur ein Teilpaket übergeben wird. – Endian

+0

@Endian: Warum reicht es nicht einfach den Netzwerk-Stream statt ein Paket? –

+0

@ Jon: Packet mehr im Sinne eines Stücks, das mein binaryReader lesen kann, ohne einen Buffer Underrun zu bekommen. Ich habe den ursprünglichen Beitrag mit dem, was ich gerade mache, nach deinem Vorschlag bearbeitet und es scheint zu funktionieren, danke für die Hilfe. :) – Endian