【问题标题】:Missing bytes in TCP file transferTCP 文件传输中缺少字节
【发布时间】:2012-01-12 07:19:35
【问题描述】:

我需要能够读取文件,将其分成任意大小的数据包,比如说 512 字节,然后通过 TCP 发送这些数据包。问题是,接收方没有收到我发送的所有字节。如果我发送 1000 个数据包,接收方在从 InputStream 读取时会阻塞,因为他没有更多数据要读取大约 990 个数据包左右。

这是代码(只是发送和接收部分):

发件人:

int parts = (int)Math.ceil((double)(file.length()/512.0));

out.println(parts+"");
int readFile;
int i = 0;
while ((readFile = fileIn.read(buffer)) != -1) {
   i++;
   fileOut.write(buffer, 0, readFile);
   fileOut.flush();
   System.out.println("-- Sent packet " + i + "/" + parts + ". " + "Bytes sent = " + readFile);
}

接收者:

int parts = Integer.parseInt(in.readLine());
byte[] buffer = new byte[512];
FileOutputStream pw = new FileOutputStream("file.ext");
DataInputStream fileIn = new DataInputStream(socket.getInputStream());
for(int j = 0; j < parts; j++){
   int read = 0;
   if(j == parts - 1){
      read = fileIn.read(buffer);
      pw.write(buffer, 0, read);
   }else{
      fileIn.readFully(buffer);
      pw.write(buffer);
   }
   System.out.println("-- Received packet " + (j+1) + "/" + parts + ". Read " +read+ " bytes.");
}

我尝试增加套接字的发送和接收缓冲区大小,但没有成功。我错过了什么?

这是一个输出示例:

发件人:

-- Sent packet 1/10. Bytes sent = 512
-- Sent packet 2/10. Bytes sent = 512
-- Sent packet 3/10. Bytes sent = 512
-- Sent packet 4/10. Bytes sent = 512
-- Sent packet 5/10. Bytes sent = 512
-- Sent packet 6/10. Bytes sent = 512
-- Sent packet 7/10. Bytes sent = 512
-- Sent packet 8/10. Bytes sent = 512
-- Sent packet 9/10. Bytes sent = 512
-- Sent packet 10/10. Bytes sent = 234

接收者:

-- Received packet 1/10. Read 512 bytes.
-- Received packet 2/10. Read 512 bytes.
-- Received packet 3/10. Read 512 bytes.
-- Received packet 4/10. Read 512 bytes.
-- Received packet 5/10. Read 512 bytes.
-- Received packet 6/10. Read 512 bytes.
-- Received packet 7/10. Read 512 bytes. (And it blocks here, because there is no more data to read)

【问题讨论】:

  • 您可能永远无法 100% 确定您会以parts 的形式发送内容。你一直写,直到写完。 buffer 的大小是多少?您基本上需要深入了解并继续写入 fileOut,直到您写入缓冲区的全部内容。
  • buffer 具有所需的数据包大小,在本例中为 512 字节。根据我的输出,文件总是设法在parts 数据包中发送。
  • 我认为你的接收器坏了。你不应该相信你会收到parts 读取的所有内容。只需循环,直到您有要阅读的内容...
  • 我添加了一个示例输出。
  • 我试过了,但问题仍然存在......例如,如果我发送一个 1213234 字节的文件,大多数情况下接收者会阻塞这条消息-- Read 314 bytes. (1206066/1213234),这意味着 - (bytesRead/TotalBytes) .

标签: java tcp


【解决方案1】:

TCP 是一种流协议。没有数据包,只有一个数据流。

您不应假设单个write()(有或没有flush())将对应于单个read()。因此,您的接收循环 for(int j = 0; j &lt; parts; j++) 被误导了:更好的方法是计算读取的 字节 数量,并为 read() 调用返回不同数量的数据做好准备。

在 cmets 中,您认为 readFully() 可以解决问题。然而,我关心的不是代码,而是基于数据包的流视图。这会在您的最后一次fileIn.read(buffer) 调用中导致类似的错误。它可能会返回作为您上一个“数据包”的一部分发送的数据的一半,而您永远不会知道!

【讨论】:

  • 这就是为什么我使用 readFully() 方法而不是简单的 read()。我知道我正在通过流发送数据,我只是不想在一次发送中发送所有数据。
  • @WorkingSoHard:我认为您的整体方法存在缺陷。例如,考虑最后一个 read() [if(j == parts - 1) 中的那个]。它可能会给你发送一半的数据,而且你永远不会知道你已经截断了你的流!
  • 虽然这是一个问题并且值得修复,但它不是给我带来麻烦的那个。我将接收器更改为使用 read() 而不是 readFully() 并且问题仍然存在。我认为某些字节没有到达接收器。反正它们会被丢弃吗?
  • @WorkingSoHard:TCP 提供有保证的交付。您能否发布完整的、可运行的客户端和服务器,以便我们进行试验?
【解决方案2】:

将其分成任意大小的数据包

为什么?这是一个流协议。您可以在发送端尽可能多地分解它,但是本地 TCP 会尽力将您的写入组合成更大的 TCP 段;然后,本地 IP 会将其分块为 MTU 大小的 IP 数据包;中间路由器可能会进一步分段;远程 IP 然后将片段重新组合成数据包;然后远程 TCP 将数据包重新组合成段;然后,远程应用程序将根据所有内容以及内核中其套接字接收缓冲区的大小以及其应用程序接收缓冲区的大小,以块的形式接收。

不要试图超越所有的处理。你不能。有事写就写吧。

如果您有“丢失字节”,那只能是因为您忽略了接收方 read() 返回的长度值,或者如果您在发送方使用非阻塞 I/O,则返回的长度值write() 在发送方。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-11-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-06-21
    • 2015-10-11
    • 2023-03-08
    • 2012-05-23
    相关资源
    最近更新 更多