我正在尝试使用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的文件?
答案 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)
您的传输协议似乎是:
int
。int
除了对@ EJP的答案中指出的文档的误解之外,基本问题在于这个协议。
您认为文件长度可以int
发送到烤箱。这意味着它发送的长度不能超过Integer.MAX_VALUE
。当然,这会限制您使用2G长度的文件(请记住Java整数已签名)。
如果您查看Files.size()
方法,这是一种获取实际文件大小(以字节为单位)的方法,您将看到它返回long
。 long
将容纳大于2GB且大于4GB的文件。事实上,您的协议至少应该定义为以 long
而不是int
字段开头。
大小问题与TCP数据包完全无关。