Search code examples
javasocketsclient-serverchatdatainputstream

Transmiting/receiving compressed data with sockets: how to properly receive the data sent from the client


I have developed a client-server chat using the Sockets and it works great, but when I try to transmit data with Deflate compression it doesn't work: the output is "empty" (actually it's not empty, but I'll explain below).

The compression/decompression part is 100% working (I have already tested it), so the problem must be elsewhere in the transmission/receiving part.

I send the message from the client to the server using these methods:

// streamOut is an instance of DataOutputStream
// message is a String

if (zip) { // zip is a boolean variable: true means that compression is active
    streamOut.write(Zip.compress(message)); // Zip.compress(String) returns a byte[] array of the compressed "message"
} else {
    // if compression isn't active, the client sends the not compressed message to the server (and this works great)
    streamOut.writeUTF(message);
}
streamOut.flush();

And I receive the message from the client to the server using these other methods:

// streamIn is an instace of DataInputStream

if (server.zip) { // same as before: true = compression is active
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    byte[] buf = new byte[512];
    int n;

    while ((n = streamIn.read(buf)) > 0) {
        bos.write(buf, 0, n);
    }

    byte[] output = bos.toByteArray();
    System.out.println("output: " + Zip.decompress(output)); // Zip.decompress(byte[]) returns a String of decompressed byte[] array received
} else {
    System.out.println("output: " + streamIn.readUTF()); // this works great
}

Debugging a little bit my program, I've discovered that the while loop never ends, so:

byte[] output = bos.toByteArray();
System.out.println("output: " + Zip.decompress(output));

is never called.

If I put those 2 lines of code in the while loop (after bos.write()), then all works fine (it prints the message sent from the client)! But I don't think that's the solution, because the byte[] array received may vary in size. Because of this I assumed that the problem is in the receiving part (the client is actually able to send data).

So my problem became the while loop in the receiving part. I tried with:

while ((n = streamIn.read(buf)) != -1) {

and even with the condition != 0, but it's the same as before: the loop never ends, so the output part is never called.


Solution

  • The read function will not return a -1 until the stream is closed. What you can do is calculate the number of bytes that should be sent from the server to the client, and then read that number of bytes on the client side.

    Calculating the number of bytes is as easy as sending the length of the byte array returned from the Zip.compress function before the actual message, and then use the readInt function to get that number.

    Using this algorithm makes sure that you read the correct number of bytes before decompressing, so even if the client actually reads 0 bytes it will continue to read until it receives all bytes it wants. You can do a streamIn.read(buf, 0, Math.min(bytesLeft, buf.length)) to only read as many bytes you want.