Java TCP-Sockets传输大于4GB的文件

时间:2015-05-27 08:00:57

标签: java sockets tcp

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

这会导致当我尝试发送大于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的文件?

2 个答案:

答案 0 :(得分:2)

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);

见上文re 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);
}

答案 1 :(得分:0)

您的传输协议似乎是:

  1. int
  2. 发送总文件长度
  3. 对于每一堆字节读取,
    1. int
    2. 中发送加密字节数
    3. 自行发送录入的字节。
  4. 除了对@ EJP的答案中指出的文档的误解之外,基本问题在于这个协议。

    您认为文件长度可以int发送到烤箱。这意味着它发送的长度不能超过Integer.MAX_VALUE。当然,这会限制您使用2G长度的文件(请记住Java整数已签名)。

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

    大小问题与TCP数据包完全无关。