2011-04-03 111 views
1

這裏是我的代碼:數據丟失TCP IP C#

private void OnReceive(IAsyncResult result) 
{ 
NetStateObject state = (NetStateObject)result.AsyncState; 

Socket client = state.Socket; 

int size = client.EndReceive(result); 

byte[] data = state.Buffer; 

object data = null; 

using (MemoryStream stream = new MemoryStream(data)) 
{ 
    BinaryFormatter formatter = new BinaryFormatter(); 

    data = formatter.Deserialize(stream); 
} 

//todo: something with data 

client.BeginReceive(
    state.Buffer, 
    0, 
    NetStateObject.BUFFER_SIZE, 
    SocketFlags.None, 
    OnReceive, 
    state 
    ); 
} 

state.Buffer具有NetStateObject.BUFFER_SIZE的最大尺寸(1024)。首先,這是太大還是太小?其次,如果我發送比這更大的內容,則反序列化會因爲它試圖反序列化的對象不具備所有信息(因爲並非所有數據都已發送)而混亂。在我嘗試構建並使用它之前,如何確保我的所有數據都已收到?

完成工作的代碼

 private void OnReceive(IAsyncResult result) 
    { 
     NetStateObject state = (NetStateObject)result.AsyncState; 

     Socket client = state.Socket; 

     try 
     { 
      //get the read data and see how many bytes we received 
      int bytesRead = client.EndReceive(result); 

      //store the data from the buffer 
      byte[] dataReceived = state.Buffer; 

      //this will hold the byte data for the number of bytes being received 
      byte[] totalBytesData = new byte[4]; 

      //load the number byte data from the data received 
      for (int i = 0; i < 4; i++) 
      { 
       totalBytesData[i] = dataReceived[i]; 
      } 

      //convert the number byte data to a numan readable integer 
      int totalBytes = BitConverter.ToInt32(totalBytesData, 0); 

      //create a new array with the length of the total bytes being received 
      byte[] data = new byte[totalBytes]; 

      //load what is in the buffer into the data[] 
      for (int i = 0; i < bytesRead - 4; i++) 
      { 
       data[i] = state.Buffer[i + 4]; 
      } 

      //receive packets from the connection until the number of bytes read is no longer less than we need 
      while (bytesRead < totalBytes + 4) 
      { 
       bytesRead += state.Socket.Receive(data, bytesRead - 4, totalBytes + 4 - bytesRead, SocketFlags.None); 
      } 

      CommandData commandData; 

      using (MemoryStream stream = new MemoryStream(data)) 
      { 
       BinaryFormatter formatter = new BinaryFormatter(); 

       commandData = (CommandData)formatter.Deserialize(stream); 
      } 

      ReceivedCommands.Enqueue(commandData); 

      client.BeginReceive(
       state.Buffer, 
       0, 
       NetStateObject.BUFFER_SIZE, 
       SocketFlags.None, 
       OnReceive, 
       state 
       ); 

      dataReceived = null; 
      totalBytesData = null; 
      data = null; 
     } 
     catch(Exception e) 
     { 
      Console.WriteLine("***********************"); 
      Console.WriteLine(e.Source); 
      Console.WriteLine("***********************"); 
      Console.WriteLine(e.Message); 
      Console.WriteLine("***********************"); 
      Console.WriteLine(e.InnerException); 
      Console.WriteLine("***********************"); 
      Console.WriteLine(e.StackTrace); 
     } 
    } 
+0

只是一個快速的想法......你是否嘗試在反序列化數據之前調用stream.Flush()?只是爲了確保數據在反序列化之前存在 – Adi 2011-04-03 07:36:18

+0

不,我沒有嘗試過;我剛剛嘗試過,沒有任何改變。 – 2011-04-03 07:42:03

回答

4

TCP是一個流協議。它沒有數據包的概念。一個寫入調用可以用多個數據包發送,多個寫入調用可以放入同一個數據包。所以你需要在TCP之上實現你自己的打包邏輯。

有兩種常用的方法打包:

  1. 定界符的字符,這通常是在文本協議中使用的,與新的行作爲一個共同的選擇
  2. 前綴長度的每個數據包,通常是用二進制協議很好的選擇。

您在該數據包的開頭存儲邏輯數據包的大小。然後,您閱讀,直到您收到足夠的字節來填充數據包並開始反序列化。

+0

我如何在長度上加前綴?這似乎是要走的路。我在想:序列化數據,獲取字節長度,序列化長度,創建新的字節數組,首先放置長度,數據第二。發送數據。唯一的麻煩是,我不知道有多遠,我知道我有足夠的數據重建前綴。 – 2011-04-03 08:05:51

+0

您可以使前綴不變,例如4個字節。 – CodesInChaos 2011-04-03 08:11:47

+0

得到它的工作。非常感謝你! – 2011-04-03 19:54:00

4

在我嘗試構建它並對它做某些事情之前,如何確保我的所有數據都已收到?

你必須實現一些協議,所以你知道。儘管TCP是可靠的,但它並不能保證來自套接字一端的單次寫入的數據在另一端將顯示爲單個讀取:重試,數據包分段和MTU都可以導致數據在接收器的尺寸不同。您將以正確的順序獲取數據。

因此,您需要在發送時包含一些信息,以便接收方知道它何時具有完整的消息。我還建議包括什麼樣的消息和什麼版本的數據(這將形成能夠一起支持不同客戶端和服務器版本的基礎)。

所以發送方發送: - 消息類型 - 消息版本 - 消息大小(以字節計)

並且接收機將環,具有緩衝執行讀取和附加這個到主緩衝液(MemoryStream對此很好)。一旦收到完整的標題,它就知道何時收到完整的數據。 (另一種途徑是將某種模式作爲「消息結束」標記,但是如果數據是二進制文本而不是文本,則需要處理內容中出現的相同字節序列)。

+0

得到它的工作謝謝你! – 2011-04-03 19:54:16