Search code examples
javasocketsserverclient

Sending multiple photos from server to client stuck in a while loop


I'm building a server/client application that consists of sending and saving information about photos.

I've managed to successfully send 1 photo from the client to the server at a time. Now, I'm trying to get the server to send the client multiple photos.

I used the same code I used before and put it in a for loop. However, when I run the code, the client side gets stuck on the while loop. What's especially weird is that, if I ask the server to send 3 photos, the client successfully receives the 3 and completes the first 2 loops, both the while and the for, correctly (I observed this with some prints on the console). However, it does not leave the while loop on the last iteration. I also noticed that, if I close the server, the client is able to finish the loop.

I would like to know why the client is getting stuck on the last iteration of the while loop, even though it seems like it's correctly receiving the entirety of the images (I can open them).

The code I used on the server side is the following:

for (int i=0; i<n;i++) {
    try {
        //photo
        File f = new File("photo.jpg");
        
        BufferedInputStream reader = new BufferedInputStream( new FileInputStream( f ) );
                    
        //sending the photo
        byte[] buffer = new byte[ 4096 ];
        int bytesRead;
        while ( (bytesRead = reader.read(buffer)) != -1 ) {
            //ObjectOutputStream outStream is being passed to the function as an argument
            outStream.write( buffer, 0, bytesRead );
            }
        reader.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

It's somewhat simplified, because in reality I'm iterating through a list of photos and updating f on each cycle.

The code on the client side looks like this:

for (int j=0; j<n; j++) {
    try {
        File result = new File("fromServer.jpg");
        BufferedOutputStream out2 = new BufferedOutputStream( new FileOutputStream( result ) );
                
        byte[] buffer = new byte[ 4096 ];
        int bytesRead;
        //ObjectInputStream in was created earlier in the code, inside the same function
        while ( (bytesRead = in.read( buffer )) != -1 ) {
            out2.write( buffer, 0, bytesRead );
        }
        out2.flush();
        out2.close();
        } catch (IOException e3) {
            e3.printStackTrace();
        }
}

Once again, I simplified the code because the file name and location is also updated on each iteration but this is the core of the idea.

I would like to know why the client doesn't leave the while loop on the last image. Thank you in advance.


Solution

  • The approach shown will not work for multiple files. You need to indicate where one file ends and the next begins. For instance, first send the number of files (so the receiver knows how many times it needs to loop), and then for each file, send the file's size before sending the file's bytes (so the receiver knows when to stop reading a file and prepare for the next file).

    Try something more like this:

    try {
        outStream.writeInt(n);
    
        for (int i = 0; i < n; i++) {
            File f = new File("photo.jpg");
    
            long fileSize = f.length();
            outStream.writeLong(fileSize);
    
            BufferedInputStream reader = new BufferedInputStream( new FileInputStream( f ) );
            byte[] buffer = new byte[ 4096 ];
            int bytesRead;
            long curr = 0;
    
            while (curr < fileSize) {
                bytesRead = reader.read(buffer, 0, (int) Math.min(fileSize - curr, (long) 4096));
                if (bytesRead == -1) {
                    // premature EOF, receiver is expecting more bytes
                    // than are actually available (should not happen, unless
                    // someone else is manipulating the file while we have it
                    // open), so just fail here ...
                    throw something;
                }
                outStream.write( buffer, 0, bytesRead );
                curr += bytesRead;
            }
    
            reader.close();
        }
    }
    catch (IOException e) {
        e.printStackTrace();
    }
    
    try {
        int n = in.ReadInt(n);
    
        for (int j = 0; j < n; j++) {
            long fileSize = in.readLong();
    
            File result = new File("fromServer" + Integer.toString(j) + ".jpg");
            BufferedOutputStream out2 = new BufferedOutputStream( new FileOutputStream( result ) );                
            byte[] buffer = new byte[ 4096 ];
            int bytesRead;
            long curr = 0;
    
            while (curr < fileSize) {
                bytesRead = in.read(buffer, 0, (int) Math.min(fileSize - curr, (long) 4096));
                if (bytesRead == -1) {
                    // sender disconnected prematurely, just fail here ...
                    throw something;
                }
                out2.write(buffer, 0, bytesRead);
                curr += bytesRead;
            }
            out2.flush();
            out2.close();
        }
    }
    catch (IOException e3) {
        e3.printStackTrace();
    }