2011-04-03 85 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