【问题标题】:Java TCP-Sockets transmit files larger than 4gb [duplicate]Java TCP-Sockets传输大于4gb的文件[重复]
【发布时间】:2015-05-27 08:00:56
【问题描述】:

我正在尝试使用 Java SocketsAPI 传输大于 4gb 的文件。我已经通过 InputStreams 读取它并通过 OutputStreams 写入它。但是,在 Wireshark 中分析传输的数据包时,我意识到 TCP 数据包的序列号会增加数据包的字节长度,这似乎是 1440 字节。

这导致当我尝试发送大于 4gb 的文件时,超出 TCP 的 Sequence-Number 字段的总大小,导致大量错误包,但在 Java 中没有错误。

我的传输代码目前如下所示:

 DataOutputStream fileTransmissionStream = new DataOutputStream(transmissionSocket.getOutputStream());
                    FileInputStream fis = new FileInputStream(toBeSent);
                    int totalFileSize = fis.available();
                    fileTransmissionStream.writeInt(totalFileSize);
                    while (totalFileSize >0){
                        if(totalFileSize >= FileTransmissionManagementService.splittedTransmissionSize){
                            sendBytes = new byte[FileTransmissionManagementService.splittedTransmissionSize];
                            fis.read(sendBytes);
                            totalFileSize -= FileTransmissionManagementService.splittedTransmissionSize;
                        } else {
                            sendBytes = new byte[totalFileSize];
                            fis.read(sendBytes);
                            totalFileSize = 0;
                        }
                        byte[] encryptedBytes = DataEncryptor.encrypt(sendBytes);
                        /*byte[] bytesx = ByteBuffer.allocate(4).putInt(encryptedBytes.length).array();
                        fileTransmissionStream.write(bytesx,0,4);*/
                        fileTransmissionStream.writeInt(encryptedBytes.length);
                        fileTransmissionStream.write(encryptedBytes, 0, encryptedBytes.length);

在这种情况下我到底做错了什么,或者无法通过一个 Socket 传输大于 4gb 的文件?

【问题讨论】:

标签: java sockets tcp


【解决方案1】:

TCP 可以处理无限长的数据流。序列号环绕没有问题。由于它最初是随机的,因此几乎可以立即发生,无论流的长度如何。问题出在您的代码中:

DataOutputStream fileTransmissionStream = new DataOutputStream(transmissionSocket.getOutputStream());
FileInputStream fis = new FileInputStream(toBeSent);
int totalFileSize = fis.available();

available(). 的经典误用 查看 Javadoc,了解它的真正用途。这也是您的基本问题所在,因为值 > 2G 不适合int,,因此存在截断。您应该使用 File.length(), 并将其存储到 long.

fileTransmissionStream.writeInt(totalFileSize);
while (totalFileSize >0){
    if(totalFileSize >= FileTransmissionManagementService.splittedTransmissionSize){
        sendBytes = new byte[FileTransmissionManagementService.splittedTransmissionSize];
        fis.read(sendBytes);

在这里你忽略了read() 的结果。不能保证填充缓冲区:这就是它返回值的原因。再次参见 Javadoc。

        totalFileSize -= FileTransmissionManagementService.splittedTransmissionSize;
    } else {
        sendBytes = new byte[totalFileSize];

在这里,您假设文件大小适合 int, 并假设字节适合内存。

        fis.read(sendBytes);

见上文read().

        totalFileSize = 0;
    }
    byte[] encryptedBytes = DataEncryptor.encrypt(sendBytes);
    /*byte[] bytesx = ByteBuffer.allocate(4).putInt(encryptedBytes.length).array();
    fileTransmissionStream.write(bytesx,0,4);*/

我们对您注释掉的代码不感兴趣。

    fileTransmissionStream.writeInt(encryptedBytes.length);
    fileTransmissionStream.write(encryptedBytes, 0, encryptedBytes.length);

你不需要所有这些垃圾。使用CipherOutputStream 来处理加密,或者更好的是 SSL,并使用以下复制循环:

byte[] buffer = new byte[8192]; // or much more if you like, but there are diminishing returns
int count;
while ((count = in.read(buffer)) > 0)
{
    out.write(buffer, 0, count);
}

【讨论】:

    【解决方案2】:

    看来你的传输协议是:

    1. int 中发送总文件长度。
    2. 对于读取的每一串字节,
      1. int 中提前发送加密字节数,
      2. 自行发送入口字节。

    除了@EJP 的回答中指出的对文档的误解之外,基本问题在于这个协议。

    您假设文件长度可以在int 中发送。这意味着它发送的长度不能超过Integer.MAX_VALUE。当然,这会将您限制为 2G 长度的文件(记住 Java 整数是有符号的)。

    如果您查看Files.size() 方法,这是一种以字节为单位获取实际文件大小的方法,您会看到它返回longlong 将容纳大于 2GB 和大于 4GB 的文件。所以实际上,您的协议至少应该定义为以 long 而不是 int 字段开头。

    大小问题真的与 TCP 数据包无关。

    【讨论】:

      猜你喜欢
      • 2021-11-15
      • 1970-01-01
      • 2010-09-11
      • 1970-01-01
      • 2018-01-21
      • 2018-09-05
      • 1970-01-01
      • 2019-12-18
      • 2018-10-10
      相关资源
      最近更新 更多