Sending java objects as bytes over a http request: is BASE64 necessary? - java

I have a Java object that is serializable. I need to let another Java webapp do something with this object. I'm currently using this setup to transfer the object:
Use a ByteArrayOutputStream baos
Put the baos in an ObjectOutputStream oos
Write the object using the oos, and get the byte[] from the baos
Use a Base64 encoder to encode the byte[] to another byte[]
send the bytes using a httpurlconnection (POST method)
at the receiving end, I do steps 1 to 4 backwards to get my object back.
This works! However, I'm not sure what step 4, the BASE64 encoding is really doing here. Can't I just send the plain bytes in request's body? What are advantages / safety measures that I get with using the Base64 encoding/decoding?
Here's the relevant code snippet described in step 1-4 I found in this other question.
/** Read the object from Base64 string. */
private static Object fromString( String s ) throws IOException, ClassNotFoundException {
byte [] data = Base64.getDecoder().decode( s );
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream( data ) );
Object o = ois.readObject();
ois.close();
return o;
}
/** Write the object to a Base64 string. */
private static String toString( Serializable o ) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream( baos );
oos.writeObject( o );
oos.close();
return Base64.getEncoder().encodeToString(baos.toByteArray());
}
}

The purpose of Base64 is to convert a byte array into a human readable (and universally compatible) format.
Because the underlying stream sends data via bytes, and there is no risk of losing data (eg printing to console a control character is not copy/pasteable), there is no point in converting the bytes to Base64. You get no benefits except for possible future compatibility if you change your network protocol to something else that suffers from being unable to transmit non-printable chars.

Related

Encode object in Java decode in Android

Is there any difference between Java and Android Base64 encode/decode ?
I'm trying to encode an object on my java server and decode on my android app, but getting something completely different.
(I know that it can be done with JSON).
On the JAVA Server :
public static String toString( Serializable o ) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream( baos );
oos.writeObject( o );
oos.close();
return Base64.getEncoder().encodeToString(baos.toByteArray());
}
On Android app:
public static Object fromString( String s ) throws IOException, ClassNotFoundException {
byte [] data = Base64.decode( s, Base64.NO_WRAP);
ObjectInputStream ois = new ObjectInputStream(
new ByteArrayInputStream( data ) );
Object o = ois.readObject();
ois.close();
return o;
}
Some more data:
The test object is
new String("igor")
encoded in java : rO0ABXQABGlnb3I=
decoded in android : data = [-84 -19 0 5 116 0 4 105 103 111 114]
The function crushes on ois.readObject();
Is there any other way to send objects from server to client ?
To send an object from the java server to the android app :
Create your class and implements Serializable in the java project on the server.
Generate the serialversionuid to the class , this serialversionuid
will guaranty that the same object is recived/sended , you can do
that on eclpise by putting the mouse over the class name and press generate
serial verison id.
Export your class as a jar and save it somewhere on your disk drive.
Delete the class from your project.
Import the jar witch you created in both android and java.
Use ObjectOutputStream/ObjectInputStream and all the methods related
to send the object.
To read the object that has been send :
Myclass m = (Myclass) ObjectInputStream.readObject();
Please Note this method Can be done using sockets , If you want to send it over http you can send it as a json format and use the Gson library to convert the json string to an object.

Invalid stream header 434B0005 from client to server

I've serialized some objects so I can convert them to byte arrays for a TCP packet. When I send the objects from the server program to the client program, there are no issues and it works fine. However, even though the code between the server and client is identical, when I try to send objects from the client to the server I get an invalid header.
Here are the objects I'm serializing:
public static byte[] serialize(Hand c) throws IOException
{
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(c);
return baos.toByteArray();
}
public static Hand deserialize(byte[] bytes) throws IOException, ClassNotFoundException
{
ByteArrayInputStream b = new ByteArrayInputStream(bytes);
ObjectInputStream o = new ObjectInputStream(b);
return (Hand) o.readObject();
}
and
public static byte[] serialize(Card c) throws IOException
{
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(c);
return baos.toByteArray();
}
public static Card deserialize(byte[] bytes) throws IOException, ClassNotFoundException
{
ByteArrayInputStream b = new ByteArrayInputStream(bytes);
ObjectInputStream o = new ObjectInputStream(b);
return (Card) o.readObject();
}
Those are both taken from the Server program, but the code for the serialization is identical between the server and the client; I important the Card class and the Hand class from the Server to the Client precisely to make sure errors like this wouldn't occur.
The server can convert a Card or a Hand to a byte[] and write it over a DataOutputStream to the client, and the client can receive the Card or Hand through a DataInputStream, deserialize it, and read it with no problem. When I try to send a Card or a Hand from the Client to the Server, however, very rarely it works and usually I get a
Exception in thread "main" java.io.StreamCorruptedException: invalid stream header: 434B0005
at java.io.ObjectInputStream.readStreamHeader(Unknown Source)
at java.io.ObjectInputStream.<init>(Unknown Source)
at Hand.deserialize(Hand.java:29)
at KoiKoi_TCP_Server.takeClientTurn(KoiKoi_TCP_Server.java:321)
at KoiKoi_TCP_Server.main(KoiKoi_TCP_Server.java:380)
where Hand.java.29 points at the line
ObjectInputStream o = new ObjectInputStream(b);
in the Hand deserialization method.
I understand that it's telling me that the header is invalid. I'm not sure how to fix it, because it only breaks going one direction and the code is identical. Suggestions?
I'm only sending a solitary object at a time, so I'm not initializing multiple ObjectInputStreams or anything.
I'm not initializing multiple ObjectOutputStreams or anything.
Yes you are. You are initializing a new ObjectOutputStream for every object, and then you're giving yourself the additional problem of knowing how many bytes to read in order to receive each object, and you're getting that wrong, so you're getting out of sync.
Get rid of all this. You don't need it. It is just adding problems. Just use a single ObjectOutputStream and ObjectInputStream, directly, for the life of the socket, constructed directly over the socket streams, and call writeObject() when you want to send an object, and readObject() when you want to read one. Two lines of code. Forget about the byte arrays and the ByteArray/DataInput/OutputStreams altogether.

File transfer and object serialization in Java

Im working on a Client/server chat application which allows user to send files (images / videos...) through a socket connection.
In order to manage all kind of communication, I use an Object "Packet" which stores all information that I want to send. (Sender, receivers, file ...).
Here is a code sample where I write in the stream :
private void write(Packet packet) throws IOException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream os = new ObjectOutputStream(bos);
os.writeObject(packet);
this.outStream.write(bos.toByteArray());
}
And outStream is an OutputStream.
Here is my Connection run :
public void run() {
while (isRunning()) {
try {
byte[] buffer = new byte[65536];
// Read from the InputStream
inStream.read(buffer);
ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(buffer));
Packet p = (Packet) in.readObject();
} catch (IOException e) {
e.printStackTrace();
this.disconnect();
}
}
}
It works very well for all purpose except files transfer !
I put the file in a byte[] (with filestream) and store the array in my Packet Object.
When the server receive the communication it breaks on the "in.readObject()" and give me a pretty "java io streamcorruptedexception wrong format : 0" exception.
I tried the transfer with a custom byte[] (filled by a string.getBytes()) and it worked very well.
So, what am I doing wrong ?
You're reading from the InputStream to a byte array (with an arbitrary size which could be too small). Then you construct an ObjectInputStream to read from this byte array. Why don't you read your object directly from the InputStream?
ObjectInputStream in = new ObjectInputStream(inStream);
Packet p = (Packet) in.readObject();
No need for a buffer.
Moreover, InputStream.read() doesn't read everything from the InputStream. It reads what is available, and returns the number of bytes read. If you don't loop until it returns -1, you only read a part of what has been sent on the other side.
BTW, you're doing the same mistake on the sending side. Instead of writing your object directly to the output stream, you write it to a byte array, adn then send this byte array. Write your object directly to the stream:
ObjectOutputStream os = new ObjectOutputStream(this.outputStream);
os.writeObject(packet);
No need for a buffer.

Hex encoded form of byte array different to same byte array “object” converted to byte array. Why?

This question is asked out of curiousity rather than any urgent need. I found some code to convert an object to a byte array (I thought I needed it at the time).
Using commons-codec I noticed that the encoded Hex string representation of the pure byte array was different to what I got if I passed the byte array through the "toByteArray" method below. I noticed that the longer version ends with the shorter version of the Hex string representation.
Instinctively this does not seem right, why does this happen?
What do the extra bytes found via the "toByteArray" method of conversion represent?
I'm guessing it is something to do with encoding?
Many Thanks, I hope this isn't too much of a newbie question.
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import org.apache.commons.codec.binary.Hex;
public class Test {
public static void main(String[] args) throws IOException {
byte[] bytes = "Stackoverflow".getBytes();
System.out.println(Hex.encodeHexString(bytes));
System.out.println(Hex.encodeHexString(toByteArray(bytes)));
}
public static byte[] toByteArray(Object obj) throws IOException {
byte[] bytes = null;
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(obj);
oos.flush();
oos.close();
bos.close();
bytes = bos.toByteArray();
return bytes;
}
}
RESULT
537461636b6f766572666c6f77
aced0005757200025b42acf317f8060854e002000078700000000d537461636b6f766572666c6f77
The second encoding is much longer because it is an object serialization format whereas the first case is just the content. The Object serialization has the header, the type of the object and finally the content in the object (you will notice the end is the same)
An ObjectOutputStream is able to serialize any type of object (as long as it is Serializable). So, it can't really map any byte[] to itself, since this would imply there is no room for other objects.

java serialized messages dont contain the correct data in socket-client-server model

hello i have a basic client-server system running using java sockets.
my problem is, that an object that i send from the client to the server does not contain the correct data after it has been sent once.
the first time i send it, it arrives with the correct values, but when i send it another time with different values, it still arrives at the server with the same values as the first time. it also happens if i send a completely different instance of that class. it always arrives with the data, which have been sent the very first time.
when i try this with other objects like java.lang.String it seems to work.
the problematic class looks like this:
public class Vector3f implements Serializable {
private static final long serialVersionUID = 2838034155614698213L;
public float x, y, z;
}
i use objectinputstream and objectoutputstream on both the server and the client to send and receive objects.
let me know, if you need any more information about the system.
thanks!
My guess is that you're changing the values of the fields and then retransmitting the same object. The ObjectOutputStream will notice that it's already sent the original object, and just send a reference the second time.
You could avoid this by calling reset() on the ObjectOutputStream - but I'd be tempted to just use separate instances anyway, possibly even making the class immutable. (Public mutable fields are almost never a good idea.)
The best way in case of serialization you should convert the object into a byte array object and then write into the socket.
// Serialize to a file
ObjectOutput out = new ObjectOutputStream(new FileOutputStream("filename.ser"));
out.writeObject(object);
out.close();
// Serialize to a byte array
ByteArrayOutputStream bos = new ByteArrayOutputStream() ;
out = new ObjectOutputStream(bos) ;
out.writeObject(object);
out.close();
// Get the bytes of the serialized object
byte[] buf = bos.toByteArray();
// Deserialize from a file
File file = new File("filename.ser");
ObjectInputStream in = new ObjectInputStream(new FileInputStream(file));
// Deserialize the object
Object obj = (Object) in.readObject();
in.close();
// Get some byte array data
byte[] bytes = getBytesFromFile(file);
// see Reading a File into a Byte Array for the implementation of this method
// Deserialize from a byte array
in = new ObjectInputStream(new ByteArrayInputStream(bytes));
in.close();

Categories

Resources