3

I have a file sender and receiver. From the 10 or so posts I've found on the internet, this is the correct way to send files through sockets in Java. Take a look at the code snippets below.

Sender:

OutputStream bos = clientSocket.getOutputStream();
FileInputStream fis = new FileInputStream(sendingFile);
BufferedInputStream bis = new BufferedInputStream(fis);

int n;
int total = 0;
byte[] buffer = new byte[8192];

while ((n = bis.read(buffer)) > 0) {
    bos.write(buffer, 0, n);
    System.out.println("Sending File... ");
    total += n;
    System.out.println(total);
}

Receiver:

InputStream bis = clientSocket.getInputStream();
FileOutputStream fos = new FileOutputStream(fileOut);
BufferedOutputStream bos = new BufferedOutputStream(fos);

int n;
int total = 0;
byte[] buffer = new byte[8192];
while ((n = bis.read(buffer)) > -1) {
    bos.write(buffer, 0, buffer.length);
    System.out.println("Writing File... ");
    total += n;
    System.out.println(total);
    if (total == fileSize) {
        break;
    }
}
System.out.println("File Received");

Here's the output from the Sender

Sending File: D:\Users\Administrator\Documents\AAAAAAAAAAAAAAAAAAAAAAAAAAA.avi
File Size: 54236160
Sending File...
8192
Sending File...
16384
....................some time later
Sending File...
54231040
Sending File...
54236160
File Sent

The last number, 54236160, is the exact size of the file being sent. Now here is the last line on the receiver side.

Writing file...
54234710

So for this particular file, it always stops at this size and so because the entire file isn't sent, the receiver never stops waiting for data. I dont understand how the sender is sending the correct amount of data but the receiver doesn't get it all. I never see "File Received" on the receiver side and the amount of data that the receiver reads is never equal to the amount being sent.

This issue occurs for any file I sent and if I send really small files, such as those that are in bytes not kb, I dont see any "Writing File..." on the receiver side at all, almost like it just ignores the stream.

Yet every post on the internet says this is the proper way to send files. Oh and if I close the sending socket and/or stream, which I dont want to do because there are other things the client and server need to do, it still doesnt fix the problem anyway.

The other thing I've noticed is that while the sender always seems to indicate its writing the full amount to the buffer (always multiples of 8192 for "total"), the receiver doesnt.

Below is the code that gets the filesize and filename. Perhaps this is where the error lies? I dont see how since its all done before we start sending and receiving the file.

System.out.println("Receiving File");
try {
    clientScanner.nextLine();
    String fileName = clientScanner.nextLine();
    int fileSize = clientScanner.nextInt();
    System.out.println("File: " + fileName + " Size: " + fileSize);
    File fileOut = new File("C:\\Users\\owner\\AppData\\Local\\temp\\" + fileName);

    InputStream bis = clientSocket.getInputStream();
    FileOutputStream fos = new FileOutputStream(fileOut);
    BufferedOutputStream bos = new BufferedOutputStream(fos);
Tom
  • 16,842
  • 17
  • 45
  • 54
Richard Chase
  • 407
  • 5
  • 21
  • 2
    Are you ever closing the writing side? By the way, `bos.write(buffer, 0, buffer.length);` should be `bos.write(buffer, 0, n);` in your copying code. – Jon Skeet Jul 26 '15 at 21:38
  • I'm not closing it because closing it will close the entire socket and I need to keep it open to do other things. Also aboutthe buffer.length, I had previously had it as "n" but changed to "buffer.length" because I saw that in other code. In either case though it doesnt work. But even if I do close the writing side using bos.close(); the receiver still doesnt get the whole file. – Richard Chase Jul 26 '15 at 21:40
  • For arguments sake, from the sending side I closed bis, fis, bos and clientSocket. What I see on the receiever end is the same last line and then "File Receieved" but the same number of 54234710 is there and the sent file is unreadable and not the correct size. In fact, the size of the file is 54229590 which is the second last count, before 54234710. – Richard Chase Jul 26 '15 at 21:49
  • 3
    I thought you said the exact size of the file was 54229590? How are you transmitting the size of the file anyway? And what do you mean by "for arguments sake"? If you *aren't* genuinely closing the socket, that would explain the hang. Oh, and the file won't be correct while the bug I mentioned in the first comment is still present. – Jon Skeet Jul 26 '15 at 21:52
  • The size of the file is 54236160. The size of the file is being transmitted through a printwriter and is read by a scanner. This is done before the file send/receive begins. WHat I should have said instead of "for arguments sake" was that I rewrote the code to close the sockets, as a test, and it still didnt work. The "bug" you mentioned was corrected immediately after I saw your comment and was not present until about 10 befores before I posted this original question as I tried it just during one compile. – Richard Chase Jul 26 '15 at 21:57
  • 1
    So what happens when you *do* close the socket? I'd be very surprised if you didn't see *any* change in behaviour? And if you *don't* want to close the socket, you shouldn't try to read any more data than you're expecting - in other words, change your `read` call to only read enough data to get up to `total` (but also no more than the length of the buffer, of course). – Jon Skeet Jul 26 '15 at 21:58
  • But it never gets up to the total. There is a line of code there that says if total == fileSize { break; } which means if it did read the entire file, it would exit the loop and it never does. When I close the socket, nothing changes. However one thing to note that I never noticed before is that the file size after everything finishes or stops doing anything is 5429590 even though the last reported number is 54234710. With the actual file size being 54236160, which is between those 2 numbers, that tells me that perhaps the entire file IS being read and then more data from who knows where. – Richard Chase Jul 26 '15 at 22:04
  • So then the solution is to do what you suggested, which is to change my read call to read enough data to get up to total. I thought I was doing this with the if (total == fileSize) { break;} but what is happening is that it is reading more than fileSize. How would I change the code appropriately? – Richard Chase Jul 26 '15 at 22:05
  • 1
    Will add that as an answer. I'm still surprised that closing the socket doesn't terminate the loop though. – Jon Skeet Jul 26 '15 at 22:06
  • It did close the cloop, but the file size was 54229590. I just realized I made a mistake in my assumption above. I was thinking it was 5429 but its 54229 which is NOT higher than the fileSize so its not reading more data than the filesize. So the original problem still stands that the receiver is not reading all of the data. – Richard Chase Jul 26 '15 at 22:08
  • 2
    "It did close the cloop, but the file size was 54229590." So when you said "When I close the socket, nothing changes." that wasn't true. Aargh... – Jon Skeet Jul 26 '15 at 22:08
  • Yes I guess I was wrong, but since the file size didnt change and the file transfer still didnt complete, that is what I meant by nothing changed. I can only assume the loop ended because once the socket was closed then technically the buffer would return -1 would it not even though the file transfer was not completed. – Richard Chase Jul 26 '15 at 22:15
  • 2
    Please don't assume anything in future - and don't say "nothing's changed" when you only mean "aspects X and Y didn't change." Observe, and record those observations precisely. It's hard enough trying to help with remote debugging without getting imprecise diagnostic reports. – Jon Skeet Jul 26 '15 at 22:17

1 Answers1

5

You shouldn't try to read more data than expected. Your loop should be:

int n;
int bytesLeft = fileSize;
byte[] buffer = new byte[8192];
while (bytesLeft > 0) {
    int n = bis.read(buffer, 0, Math.min(buffer.length, bytesLeft));
    if (n < 0) {
        throw new EOFException("Expected " + bytesLeft + " more bytes to read");
    }
    bos.write(buffer, 0, n);
    System.out.println("Writing File... ");
    bytesLeft -= n;
    System.out.println(total " bytes left to read");
}

(You can adjust the output to show how many bytes you've read instead of how many you've got left to read if you want, but I think this approach is simpler.)

If that still doesn't get you all the data, you should flush the output stream in the writing code - I wouldn't expect you'd have to, but if you're not closing the socket, I guess it could be buffering it forever.

If the loop terminates but the file appears to be incomplete, make sure you're closing bos after all of this.

If none of that helps, then it's possible that something else has read the start of your data before you get into this loop. You should compare the data in the output file with the original file. Look at the first (say) 16 bytes in the output file, and check that they're at the start of the input file. If they're not, then that suggests that the problem is with what you're doing with the connection before the code you've shown us. (For example, the code used to send and receive the expected file size.)

Now that we can see how you're sending / receiving the file size, that's definitely the problem. The scanner is reading some of the file data. To fix this, just use DataOutputStream on the sending side and DataInputStream on the receiving side:

DataOutputStream output = new DataOutputStream(clientSocket.getOutputStream());
output.writeUTF(fileName);
output.writeInt(fileSize);
// Write the data as before, to output

On the receiving side:

DataInputStream input = new DataOutputStream(clientSocket.getInputStream());
String fileName = input.readUTF(name);
int fileSize = input.readInt();
// Read the data before, as above, from input
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • So with this new code that I just tried, it does get down to 0 bytes left to read and then the loop terminates however the file size after its complete is 54231040 and the file itself is unreadable. The difference in file size is 5120 if that means anything. It actually doesnt make any sense that its reporting 0 bytes left to read but doesnt read all of the bytes. – Richard Chase Jul 26 '15 at 22:14
  • 1
    @RichardChase: Are you closing the `FileOutputStream` after all of this? If not, that could be the problem... – Jon Skeet Jul 26 '15 at 22:15
  • I ran it again and included by total counter and then where it hung was after these lines 54234710 then on the line below it said 1450 bytes left to read and it hung there. – Richard Chase Jul 26 '15 at 22:18
  • 1
    @RichardChase: In that case you should check that the first bytes are what you expect them to be. We haven't seen what else you've done on this connection or how you've read from it - that could be messing things up. I'll add that to my answer. – Jon Skeet Jul 26 '15 at 22:20
  • Well I wouldnt want to close the FileOutputStream until I receive the entire file and since I Never do that, I dont think that would help. I would probably want to close the FileOutputStream after i print "File Received" on the receiver side. – Richard Chase Jul 26 '15 at 22:20
  • 1
    @RichardChase: Well in another comment you pointed to a situation where the loop *did* terminate - or perhaps it did, as we only know about an assumption. At this point I think I've offered all the help I can without getting unduly frustrated by not being able to diagnose this myself. Look at the end of my answer for another possibility though - I suspect you're screwing up the input stream before you even start reading the data in the code you've shown. – Jon Skeet Jul 26 '15 at 22:23
  • I've added the code that precedes the previous code. It's where I read the filesize and filename and sets those accordingly. The edit is in my original post, at the very bottom. – Richard Chase Jul 26 '15 at 22:26
  • 1
    @RichardChase: Where does `clientScanner` come from though? Is that on the same socket? If so, then yes - that's the problem. The scanner will have eagerly read data. I'd advise sending the size as binary data instead, e.g. as 4 or 8 bytes. – Jon Skeet Jul 26 '15 at 22:28
  • Yes it is. Far above this code is a line clientScanner = new Scanner(clientSocket.getInputStream()); Do you think that clientScanner is eating some of my data? – Richard Chase Jul 26 '15 at 22:28
  • 4
    @RichardChase: Right. So that's the problem - and a good example of why it's really helpful to put together a short but *complete* program demonstrating the problem. Don't send/receive the size like that. Don't create two wrappers for the same input stream. You might want to use `DataInputStream` and `DataOutputStream` instead - then you can just use `writeInt` to write the length and `readInt` to read the length, then write/read the data on the same streams. – Jon Skeet Jul 26 '15 at 22:30
  • Alright. I had originally started using a scanner because its simple and I knew how to read and write simple ints and strings. However I see how that could cause some problems. I will remove the scanner and try using data input/output streams on both ends. I will have this done in the next few days and then will comment back and accept your answer as well. Thanks for your patience and I do apologize again for not providing the proper information. – Richard Chase Jul 26 '15 at 22:34
  • It worked! The only other thing I had to do was close my fileoutput stream so that it flushed the last bits of data to the file, otherwise it would write up to the second-last chunk. With the new data streams I can simplify a few other things to and make my commands a little more readable (using "MOVEMOUSE" instead of 4, for example). – Richard Chase Jul 29 '15 at 03:20
  • @EJP I dont believe this is a duplicate. The issue I was having is not the same as the duplicate thread you posted. – Richard Chase Jul 29 '15 at 03:47