6

我刚刚读到一篇文章说 TCPClient.Read() 可能无法一次读取所有发送的字节。你如何解释这一点?

例如,服务器可以将字符串写入 tcp 流。客户端读取字符串的一半字节,然后在另一个读取调用中读取另一半。

你怎么知道何时需要合并两个调用中收到的字节数组?

4

3 回答 3

18

你怎么知道何时需要合并两个调用中收到的字节数组?

您需要在协议级别决定这一点。常见的模型有四种:

  • Close-on-finish:每一方每个连接只能发送一个“消息”。发送消息后,它们关闭套接字的发送端。接收方继续读取,直到到达流的末尾。
  • 长度前缀:在每条消息之前,包括消息中的字节数。这可以是固定长度格式(例如始终为 4 个字节)或某种压缩格式(例如,每字节 7 位大小数据,最高位设置为大小数据的最后一个字节)。然后是消息本身。接收代码将读取大小,然后读取那么多字节。
  • 分块:像长度前缀一样,但在更小的块中。每个块都有长度前缀,最后一个块表示“消息结束”
  • 消息结束信号:继续阅读,直到看到消息的终止符。如果消息必须能够包含任意数据,这可能会很痛苦,因为您需要包含转义机制才能在消息中表示终止符数据。

此外,不太常见的情况是,有些协议的每条消息总是具有特定的大小——在这种情况下,您只需要继续执行,直到您读取了那么多数据。

在所有这些情况下,您基本上需要循环,将数据读取到某种缓冲区中,直到您获得足够的数据,但是您确定了这一点。您应该始终使用的返回值Read来记录您实际读取了多少字节,并始终检查它是否为 0,在这种情况下您已经到达流的末尾。

另请注意,这不仅会影响网络流 - 对于本地以外的任何内容MemoryStream(如果它在流中,它总是会一次读取您要求的尽可能多的数据),您应该假设数据可能只在多次通话过程中变得可用。

于 2012-09-26T06:08:21.237 回答
-1

你应该read()循环调用。该循环的条件将检查是否仍有任何数据可供读取。

于 2012-09-26T06:09:49.767 回答
-3

这有点难以回答,因为你永远无法知道数据何时会到达,这就是为什么我通常在聊天程序中使用线程来接收数据的原因。但是你应该可以使用类似的东西:

do{
     numberOfBytesRead = myNetworkStream.Read(myReadBuffer,
                                              0, 
                                              myReadBuffer.Length);

     myCompleteMessage.AppendFormat("{0}",
      Encoding.ASCII.GetString(myReadBuffer, 0, numberOfBytesRead));

  }
  while(myNetworkStream.DataAvailable);

看看这个来源

于 2012-09-26T06:11:15.513 回答