2009-08-12 127 views
1

嗨,我有問題使用自定義二進制協議分組數據包。 目前服務器端代碼如下所示。使用二進制協議的TCP幀

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

我一直在看包的十六進制轉儲,有時整個包是一個炮打響,讓我們說所有的20個字節。其他時候它是分散的,我如何緩衝這些數據才能夠正確地將它傳遞給我的processPacket()方法。我試圖只使用一個字節的操作碼頭,我是否應該在頭中添加一個像(ushort)contentLength一樣的東西?我試圖使協議儘可能輕量級,而且這個系統不會發送非常大的數據包(< 128字節)。

我正在測試的客戶端代碼如下所示。

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

我不知道這可能是什麼搞亂了起來,和二進制協議似乎並沒有在網絡上很多信息,尤其是在C#參與。任何評論會有幫助。 =)

解決與此,不知道它是否正確,但它似乎給我的處理程序,他們需要什麼。

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

回答

3

您應該將其視爲流。不要依賴任何特定的分塊行爲。

您需要的數據量總是相同嗎?如果沒有,你應該改變協議(如果可以的話),以字節長度爲邏輯「數據塊」加上數據前綴。

在這種情況下,您在一邊使用的是BinaryWriter,因此將BinaryReader附加到由TcpClient.GetStream()返回的NetworkStream似乎是最簡單的方法。如果你真的想一次捕獲一個塊的所有數據,那麼你應該回到我的想法是用它的長度來加前綴數據。然後循環,直到獲得所有數據。

(請確保你有足夠的數據來讀,雖然長度!如果你的前綴長度爲4個字節,你不想讀2個字節,錯過了接下來的2 ...)

+0

嗯,我會將contentLength添加到標題,謝謝。我在processPacket()中使用了一個二進制讀取器,但顯然這只是在傳遞一個部分數據包時纔會失敗。 – Endian 2009-08-12 20:02:14

+0

@Endian:你爲什麼不通過網絡流而不是數據包呢? – 2009-08-12 20:03:29

+0

@Jon:數據包更多的是我的二進制讀取器可以讀取而不會產生緩衝區溢出的塊。我已經按照我現在所做的來編輯原文,在你的建議之後,它似乎在工作,感謝您的幫助。 :) – Endian 2009-08-12 20:39:11