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

我正在发送这样的数据:

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。

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

可能是什么问题?

最佳答案

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并取回原始字符串。

您观察到的行为的解释:

即使OS的网络堆栈几乎不能保证,实际上,它通常也会为您提供一个TCP数据包通过一个Receive调用带来的数据。由于TCP的MTU(最大数据包大小)允许约1500个字节的有效负载,因此,只要字符串小于此大小,天真的代码就可以正常工作。超过此数目,它将被拆分为多个数据包,然后一个Receive将仅返回部分数据。

10-01 02:10
查看更多