我有一个客户端 - 服务器程序。
我发送的数据如下:
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。
最糟糕的是,有时数据已被完全接收!
可能是什么问题?
答案 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
将只返回部分数据。