TcpClient未正确接收数据

时间:2011-03-12 15:30:27

标签: c# .net tcpclient

我有一个客户端 - 服务器程序。

我发送的数据如下:

private void Sender(string s,TcpClient sock)
{
   try
   {
      byte[] buffer = Encoding.UTF8.GetBytes(s);
      sock.Client.Send(buffer);
   }catch{}
}

并在客户端接收如下:

byte[] buffer = new byte[PacketSize];
int size = client.Client.Receive(buffer);
String request = Encoding.UTF8.GetString(buffer, 0, size);

问题是数据总是没有完全收到,有时它只是我发送的一部分。 PacketSize是10240,这比我发送的字节多。我还在两边设置了SendBufferSize和ReceiveBufferSize。

最糟糕的是,有时数据已被完全接收!

可能是什么问题?

1 个答案:

答案 0 :(得分:7)

size返回的TcpClient.Receive值与您发送的缓冲字符串的长度不同。这是因为无法保证在您通过Receive电话回复您发送的所有数据后,在致电Send时。这种行为是TCP工作方式所固有的(它是一种流,而不是基于消息的数据协议)。

使用较大的缓冲区无法解决问题,因为您提供的缓冲区只能限制 Receive返回的数据量。即使你提供1MB缓冲区并且有1MB的数据要读取,Receive也可以合法地返回任意数量的字节(甚至只有1)。

您需要做的是确保在调用Encoding.GetString之前缓冲了所有数据。要做到这一点,您首先需要知道有多少数据。所以至少,你需要在发送时写出字符串字节的长度:

  byte[] buffer = Encoding.UTF8.GetBytes(s);
  byte[] length = BitConverter.GetBytes(buffer.Length);
  sock.Client.Send(length);
  sock.Client.Send(buffer);

接收时,您将首先读取长度(具有已知的固定大小:4个字节),然后开始缓冲临时缓冲区中的其余数据,直到您有length个字节(这可能需要Receive次调用次数,因此您需要while次循环。只有这样,你才能打电话给Encoding.GetString并取回原来的字符串。

您观察到的行为说明:

即使操作系统的网络堆栈几乎不能保证,在实践中它通常会为您提供一个TCP数据包带来的Receive调用带来的数据。由于TCP的MTU(最大数据包大小)允许大约1500字节的有效负载,只要字符串小于此大小,天真代码就可以正常工作。除此之外,它将被分成多个数据包,然后一个Receive将只返回部分数据。