【问题标题】:Data not received correctly by TcpClientTcpClient 未正确接收数据
【发布时间】:2015-12-20 04:16:40
【问题描述】:

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

我正在发送这样的数据:

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。

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

可能是什么问题?

【问题讨论】:

    标签: c# .net tcpclient


    【解决方案1】:

    TcpClient.Receive 返回的size 值与您发送的缓冲字符串的长度不同。这是因为无法保证在调用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 并取回原来的字符串。

    您观察到的行为的解释:

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

    【讨论】:

    • +1。并且不要忘记在 tcp 客户端/流上使用超时,否则如果发送数据的应用程序行为异常/有网络问题,您的应用程序将挂起。
    • @stefan:改用Async 方法会更好。但让我们先把基础知识搞好:)
    • @Jon 使用异步可能会更好,但它确实取决于应用程序。
    • 拥有一个起始字节标识符也是一个非常好的主意,以确保您的同步。因此,当您读取了您期望的所有字节后,下一个字节应该是一个已知值,然后是下一条消息(或 EOF)。
    • @FlappySocks:为什么? TCP 保证接收到的数据与发送的完全相同,或者根本不接收。魔法标记可能会让你感觉更自信,但有技术原因吗?
    猜你喜欢
    • 1970-01-01
    • 2021-10-12
    • 1970-01-01
    • 2019-06-02
    • 2020-09-07
    • 1970-01-01
    • 2016-02-17
    • 1970-01-01
    • 2017-05-10
    相关资源
    最近更新 更多