RMI compressed socket without GZIP ZIP Streams

Hello !

I'm working on a RMI project using compressed sockets

I read a lot first and it seems to be impossible to compress a socket with (G)ZIP streams because RMI use byte mode to communicate and (G)ZIP streams are in block mode.

so, the best solution I see in this forum is to try to implement your own compressing method to create a compressed byte mode stream.

but it's hard and takes a long time to do it.

so I try something more simple unsing inflater/deflater

It works fine, I print a lot of bytes to control the communication and it seems to be ok ...until the DGC startsand hang up the application !

and I don't now why !

the code is based on the example of XOR socket

http://java.sun.com/j2se/1.4.2/docs/guide/rmi/socketfactory/index.html

I only post the compressedStream files

CompressedOutputStream.java

package serveur;

import java.io.FilterOutputStream;

import java.io.OutputStream;

import java.io.IOException;

import java.util.zip.*;

publicclass CompressedOutputStreamextends FilterOutputStream

{

public CompressedOutputStream(OutputStream out)

{

super(out);

}

publicvoid write(byte[] b,int off,int len)throws IOException

{

// print data to be sended

for(int i=0 ; i<len ; i++ )

{

System.out.print(" r"+(int)(b[i]));

}

System.out.println("");

// compress

byte[] tab =newbyte[b.length];

Deflater compresser =new Deflater();

compresser.setInput(b,off,len);

compresser.finish();

int taille = compresser.deflate(tab);

System.out.println("--compressed--");

// print compressed data

for(int i=0 ; i><taille ; i++ )

{

System.out.print(" c"+(int)(tab[i]));

}

System.out.println("");

// send

try

{

System.out.println("\nenvoie "+taille+" ("+len+")\n\n");

out.write(tab,0,taille);

//out.write(b,off,len); // send uncompressed data

}

catch( Exception e )

{

e.printStackTrace();

}

}

}

CompressedInputStream.java

package serveur;

import java.io.FilterInputStream;

import java.io.InputStream;

import java.io.IOException;

import java.util.zip.*;

publicclass CompressedInputStreamextends FilterInputStream

{

public CompressedInputStream(InputStream in)

{

super(in);

}

publicint read(byte b[],int off,int len)throws IOException

{

byte[] tab =newbyte[b.length];

int taille = in.read(tab,off,len);

// read uncompressed data

//int taille = in.read(b,off,len);

//return taille;

//////////////

System.out.println(" \ntaille lue = "+taille);

if( taille < 1 )

{

return taille;

}

// print readed compressed data

for(int i=off ; i<off+taille ; i++)

{

System.out.print(" r"+tab[i]);

}

System.out.println("");

// uncompress

Inflater decompresser =new Inflater();

decompresser.setInput(tab, off, len);

int numBytes = 0;

try

{

numBytes = decompresser.inflate(b);

System.out.println("--uncompressed--");

}

catch( Exception e )

{

e.printStackTrace();

}

decompresser.end();

// print uncompressed data

for(int i=0 ; i><numBytes ; i++)

{

System.out.print(" d"+b[i]);

}

System.out.println("");

System.out.println("\nread block "+numBytes +" off "+off+" len " +len+"\n\n***\n" );

return numBytes;

}

}

>

[7311 byte] By [DarkTwina] at [2007-11-27 2:59:42]
# 1

This is barely worth doing actually. Creating a new compressor for every write means that there is no chance for it to adapt to the entire content of the data stream, so it won't actually do all that much compression. RMI won't write in chunks of more than 4096 bytes at a time. Also the content of a serialized stream is mostly binary data apart from Strings, and that doesn't compress at all. If you're passing large Strings or large byte[] or char[] arrays of text you're probably better off compressing them before passing them as arguments.

ejpa at 2007-7-12 3:40:17 > top of Java-index,Core,Core APIs...
# 2

I know that it's a bit useless, but it's a school project and I try different things such as crypted socket, compressed socket, ssl socket,etc

> "RMI won't write in chunks of more than 4096 bytes at a time"

are sure, I thought it was 8192, the var len in read methods have always the value 8192

>"and that doesn't compress at all"

for short data, the compressed size is bigger ... logical !

but for data > ~200 bytes it really compress, even if the profit is not significant .

> " you're probably better off compressing them before passing them as arguments "

good idea, you're right, it would be better !

but my question is not about the utility of compressed data, I want to understand why it works only until the DGC starts.

DarkTwina at 2007-7-12 3:40:17 > top of Java-index,Core,Core APIs...
# 3

> > "RMI won't write in chunks of more than 4096 bytes

> at a time"

> are sure, I thought it was 8192, the var len in read

> methods have always the value 8192

Those two statements aren't inconsistent, but you're right. RMI uses BufferedInputStreams and BufferedOutputStreams with 8192-byte buffers.

As to why it works 'only until DGC starts', there's no reason why DGC should break this code. There are several bugs in your CompressedInputStream however:

byte[] tab = new byte[b.length];

The array length here should be 'len', the parameter supplied, which could be < b.length, especially if offset > 0.

int taille = in.read(tab,off,len);

Using either 'off' or 'len' here is futile. Use zero and tab.length, or better still just omit them.

decompresser.setInput(tab, off, len);

Same here.

numBytes = decompresser.inflate(b);

Now here is a bad bug. Here you are ignoring both 'off' and 'len' at a point where you definitely must use them. If offset > 0 this code will definitely fail - it will read the decompressed data into the wrong part of the array. Same applies to the loop where you print the uncompressed data out: it should run from 'off' to 'off+numBytes-', not 0 to numBytes-1.

ejpa at 2007-7-12 3:40:18 > top of Java-index,Core,Core APIs...
# 4

thanks to you ejb now it's working fine !

byte[] tab = new byte[b.length];

I wrote it in the outputstream because the size of the compressed data could be bigger than the original ... and then I copy/paste in the inputstream ( mistake ! )

and the bug was in this line

numBytes = decompresser.inflate(b);

corrected by

numBytes = decompresser.inflate(b,off,len);

so RMI wasn't able to read correct data and break ( no problem with DGC )

so now, for anyone who need, there is a simple way to code a compressed socket for RMI

CompressedOutputStream.java

import java.io.FilterOutputStream;

import java.io.OutputStream;

import java.io.IOException;

import java.util.zip.*;

public class CompressedOutputStream extends FilterOutputStream

{

public CompressedOutputStream(OutputStream out)

{

super(out);

}

public void write(byte[] b,int off,int len) throws IOException

{

System.out.println(" readed data size="+len+" ");

for( int i=off ; i<off+len ; i++ )

{

System.out.print(" "+b[i]);

}

System.out.println("");

byte[] tab = new byte[b.length];

Deflater compresser = new Deflater();

compresser.setInput(b,off,len);

compresser.finish();

int taille = compresser.deflate(tab);

System.out.println(" compressed data size="+taille+" ");

for( int i=0 ; i><taille ; i++ )

{

System.out.print(" "+tab[i]);

}

System.out.println("");

System.out.println("\n send data size="+taille+"("+len+") \n\n");

out.write(tab,0,taille);

}

}

CompressedInputStream.java

import java.io.FilterInputStream;

import java.io.InputStream;

import java.io.IOException;

import java.util.zip.*;

public class CompressedInputStream extends FilterInputStream

{

public CompressedInputStream(InputStream in)

{

super(in);

}

public int read(byte b[], int off, int len) throws IOException

{

byte[] tab = new byte[len];

int taille = in.read(tab);

if( taille >< 1 )

{

System.out.println(" nothing readed ! ");

return taille;

}

System.out.println(" readed data size="+taille+" ");

for( int i=0 ; i<taille ; i++)

{

System.out.print(" "+tab[i]);

}

System.out.println("");

Inflater decompresser = new Inflater();

decompresser.setInput(tab);

int numBytes = 0;

try

{

numBytes = decompresser.inflate(b,off,len);

}

catch( DataFormatException e )

{

e.printStackTrace();

}

decompresser.end();

System.out.println(" uncompressed data size="+numBytes+" ");

for( int i=off ; i><off+numBytes ; i++)

{

System.out.print(" "+b[i]);

}

System.out.println("");

System.out.println("\n received data size="+numBytes +"("+taille+")\n\n*************************\n" );

return numBytes;

}

}

>

DarkTwina at 2007-7-12 3:40:18 > top of Java-index,Core,Core APIs...