我有一个客户端服务器程序。
我正在发送这样的数据:
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
将仅返回部分数据。