user2436815
user2436815

Reputation: 3895

BufferedInputStream hanging (not reaching end of file)

I have Java SSL/TLS server&client sockets. My client simply sends a file to the Server and Server receives it. Here are my codes:

My client method:

static boolean writeData(BufferedOutputStream bos, File data) {
    FileInputStream fis = new FileInputStream(data);
    BufferedInputStream bis = new BufferdInputStream(fis);

    byte[] bytes = new byte[512];
    int count = 0;
    while ((count = bis.read(bytes, 0, bytes.length)) > 0) {
        System.out.println("Sending file...");
        bos.write(dataByte, 0, count);
        System.out.println(count);
    }
    bos.flush();
    System.out.println("File Sent");
}

My server method:

static boolean receiveData(BufferedInputStream bis, File data) {

    byte[] bytes = new byte[512];
    int count = 0;
    while ((count = bis.read(bytes, 0, bytes.length)) > 0) {
        System.out.println("Receiving file...");
        // Do something..
        System.out.println(count);
    }
    bos.flush();
    System.out.println("File Received");
}

The problem is, the server hangs inside the while loop.. It never reaches the "File Received" message.

Even if the file is small, the bis.read() method never returns -1 at the end of file for some reason. I tested the methods with a file size of 16 bytes, and the output is as follows:

Client terminal:

> Sending file...
> 16
> File Sent

Server terminal:

> Receiving file...
> 16

As you can see, the server never reaches the "File Received" message and hangs inside the loop even after the end of stream is reached.. Can anyone guess the reason for this?

Thanks

Upvotes: 0

Views: 1609

Answers (1)

Erwin Bolwidt
Erwin Bolwidt

Reputation: 31269

Your server never detects that the file has been sent, because it checks whether you have closed the connection at the other end (the only reason why you would receive -1 bytes read).

But you never close the connection, you only flush it.

Replace bos.flush() with bos.close() in the writeData method and it should work.


If you don't want to close the connection, because you want to do more work with it, you have to add a protocol of some sort, because there is no default way to do that.

One thing you could do, which is one of the easier ways to implement this, is to send the length of the file as a 32-bit or 64-bit integer before the file.

Then the server knows how many bytes it should read before it can consider the file fully sent.


If you don't know the length of the file, there are many options. I'm not sure if there is a consensus on the most effective way to do this, but given that many protocols take different approaches, I don't think that there is.

These are just a few suggestions, which you can tune.

  1. Before any piece of data, you send the length of the data you want to send as a 32-bit bit (signed) integer. So a file will be sent as multiple pieces of data. Sending a negative number means that the previous piece was the last piece and the file has ended. (If you needed to send a piece that was larger than the maximum that you can represent in a signed 32-bit integer, you need to split it in several pieces).
  2. You think of a random number, with a long-enough length (something like 16 bytes or 32 bytes) that it will never occur in your data. You send that number before the file and when the file is done, you send it again to indicate that event. This is similar to the MIME multi-part encoding.
  3. You take a byte or a number of bytes that indicates whether the file has ended (like 0xFF). But to ensure that you can still legitimately send 0xFF as part of the file, you add the rule that 0xFF 0xFF means that the file has ended, but 0xFF 0x00 means "just a literal 0xFF" in the file.
  4. There are many more ways to do it.

Upvotes: 2

Related Questions