Chat client-server with file transfer and hybrid cryptography - java

I'm trying to realize a chat client-server (in local) that permits to exchange text and files. I utilize java.security and java.crypto to implement hybrid cryptography (and the tool Swing).
I exchange text in a serialized way (using ObjectInputStream and ObjectOutputStream), inserting it (after I crypted it with an apposite function) in byte[] format in Message (that is an object that i created and that is effectively exchanged between sockets):
import java.io.Serializable;
public class Message implements Serializable{
private static final long serialVersionUID = 1L;
byte[] data;
Message(byte[] data){
this.data = data;
}
byte[] getData(){
return data;
}
}
Chat works fine until I exchange only Message. Now I'm trying to implement file transfer (first I tried to implement file transfer from server to client, without cryptography), therefor I took a file "selectedFile" with FileChoser and I sent it to the client thanks ObjectOutputStream's method writeObject(selectedFile). On the client side I recognized if the object that is arrived is File or Message with:
class ListenFromServer extends Thread{
public void run(){
while(true){
try{
if((Message.class.isInstance(sInput.readObject()))==true){ //I verify if the recived object belongs to Message class
m=(Message) sInput.readObject();//sInput is an instance of ObjectInputStream class, connected to client socket's InputeStream
decryptMessage(m.getData()); //function that decrypts the content of m and inserts the result in a String that after I append in a text area
}
else
{
File recivedFile= (File) sInput.readObject();
File saveFile=new File(path+"/"+ recivedFile.getName());
save(recivedFile,saveFile);//function that insert the recived file in a specific folder
System.out.println("file ricevuto");
}
} catch (ClassNotFoundException ex) {
System.out.println("INFO: Classe non trovata durante la lettura dell'oggetto Messaggio: " + ex.getMessage() + "\n");
} catch(IOException e){
System.out.println("Connection closed");
break;
}
catch(NullPointerException ne){
ne.printStackTrace();
ne.getCause();
System.out.println("Errore"+ ne.getMessage());
}
The problem is that file is recived by the client only clicking twice server's "sendFile" button , moreover now this problem regards also the action of sending text to client, because client receives Message object only when I send it twice (I use two different methods to send a Message object and a File object).
This problem doesn't occur when I eliminate instruction:
if((Message.class.isInstance(sInput.readObject()))==true){
...
}
I ask you how to overcome this problem, or if there is a better way to distinguish File and Message objects in reception.

You're actually reading two objects in sequence, not one.
sInput.readObject()
This is an order to read an object. You give this twice in sequence, so that's two requests to reads different objects.
To fix this, just read the object once, test the type of the object, and cast it when appropriate:
Object inputObject = sInput.readObject(); // Read the object once
if (inputObject instanceof Message) { // If the object is a message
Message m = (Message) inputObject; // cast as a Message
... // use the Message m
} else if (inputObject instanceof File) { // else if it's a file
File f = (File) inputObject; // cast as a File
... // use the File f
}

Related

Java - Sending of ImageIcon through ObjectOutputStream works first time, but not after that

I'm working on a server/client project in Java where, at any point during the running of the program, the client can request a set of details related to a unique ID, and the server returns the relevant set of details. This is done through PrintWriter objects accessing the socket.getOutputStream, and works fine.
I am trying to also include the sending/receiving of an image from the server to the client, and have met some very strange behaviour from the program.
The methods that send and receive the images are shown below:
SERVER-SIDE:
//send image associated with item
//through ObjectOutputStream to client
private void sendItemImage(BidItem item)
{
try
{
//wrap object output stream around
//output stream to client at this socket
ObjectOutputStream imageOutput =
new ObjectOutputStream(client.getOutputStream());
//send image object to client
imageOutput.writeObject(item.getItemImage());
}
catch (IOException ioEx)
{
//alert server console
System.out.println("\nUnable to send image for item "
+ item.getItemCode() + " to "
+ bidderName + "!");
//no exit from system
//bidding can still continue
}
}
CLIENT-SIDE:
//to be displayed on GUI
private static void receiveItemImage()
{
try
{
//wrap ObjectInputStream around socket
//to receive objects sent from server
ObjectInputStream imageInput =
new ObjectInputStream(socket.getInputStream());
//read in image and store in ImageIcon instance
image = (ImageIcon) imageInput.readObject();
//re-create label object to be blank
imageLabel = new JLabel();
//remove label containing last image
imagePanel.remove(imageLabel);
//just ignores command if it does not already contain image
//apply image to label
imageLabel.setIcon(image);
//apply image to CENTER of panel
imagePanel.add(imageLabel, BorderLayout.CENTER);
}
//problem in input stream
catch (IOException ioEx)
{
//alert user
JOptionPane.showMessageDialog(
null, "Error receiving image!");
//allow system to continue
//no exit
}
//problem casting to ImageIcon type
catch (ClassNotFoundException cnfEx)
{
//alert user
JOptionPane.showMessageDialog(
null, "Error converting Object to ImageIcon!");
//allow system to continue
//no exit
}
}
So, each time there is a request, an ObjectOutpuStream and ObjectInputStream are created to handle the passing of the image, using the socket.getOutputStream/socket.getInputStream.
These methods are first called when a client connects to the server, and the first image and set of details are sent automatically. This works fine, but any subsequent attempts at requesting the image throughout the program result in the catch (IOException) clause being met, and the error messages shown above being displayed.
I cannot for the life of me work out why it would work the first time but not again after this. If anyone could point me in the right direction, that would be great!
Thanks,
Mark
You should only wrap a stream once. In this case, you can't wrap it again as this will not work for an Object Stream.
Once you are wrapping the stream only once, call ObjectOutputStream.reset() after sending an image. If you don't do this it will just pass the reference to the object again (and use a lot of memory)

pass object to another JVM using serialization - same Java version and jars (both running our app)

Updates:
For now using a Map. Class that wants to send something to other instance sends the object, the routing string.
Use an object stream, use Java serializable to write the object to servlet.
Write String first and then the object.
Receiving servlet wraps input stream around a ObjectInputStream. Reads string first and then the Object. Routing string decides were it goes.
A more generic way might have been to send a class name and its declared method or a Spring bean name, but this was enough for us.
Original question
Know the basic way but want details of steps. Also know I can use Jaxb or RMI or EJB ... but would like to do this using pure serialization to a bytearray and then encode that send it from servlet 1 in jvm 1 to servlet 2 in jvm 2 (two app server instances in same LAN, same java versions and jars set up in both J2EE apps)
Basic steps are (Approcah 1) :-
serialize any Serializable object to a byte array and make a string. Exact code see below
Base64 output of 1. Is it required to base 64 or can skip step 2?
use java.util.URLEncode.encode to encode the string
use apache http components or URL class to send from servlet 1 to 2 after naming params
on Servlet 2 J2EE framework would have already URLDecoced it, now just do reverse steps and cast to object according to param name.
Since both are our apps we would know the param name to type / class mapping. Basically looking for the fastest & most convenient way of sending objects between JVMs.
Example :
POJO class to send
package tst.ser;
import java.io.Serializable;
public class Bean1 implements Serializable {
/**
* make it 2 if add something without default handling
*/
private static final long serialVersionUID = 1L;
private String s;
public String getS() {
return s;
}
public void setS(String s) {
this.s = s;
}
}
* Utility *
package tst.ser;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.URLEncoder;
public class SerUtl {
public static String serialize(Object o) {
String s = null;
ObjectOutputStream os = null;
try {
os = new ObjectOutputStream(new ByteArrayOutputStream());
os.writeObject(o);
s = BAse64.encode(os.toByeArray());
//s = URLEncoder.encode(s, "UTF-8");//keep this for sending part
} catch (Exception e) {
// TODO: logger
e.printStackTrace();
return null;
} finally {
// close OS but is in RAM
try {
os.close();// not required in RAM
} catch (Exception e2) {// TODO: handle exception logger
}
os = null;
}
return s;
}
public static Object deserialize(String s) {
Object o = null;
ObjectInputStream is = null;
try {
// do base 64 decode if done in serialize
is = new ObjectInputStream(new ByteArrayInputStream(
Base64.decode(s)));
o = is.readObject();
} catch (Exception e) {
// TODO: logger
e.printStackTrace();
return null;
} finally {
// close OS but is in RAM
try {
is.close();// not required in RAM
} catch (Exception e2) {// TODO: handle exception logger
}
is = null;
}
return o;
}
}
**** sample sending servlet ***
Bean1 b = new Bean1(); b.setS("asdd");
String s = SerUtl.serialize(b);
//do UrlEncode.encode here if sending lib does not.
HttpParam p = new HttpParam ("bean1", s);
//http components send obj
**** sample receiving servlet ***
String s = request.getParameter("bean1");
Bean1 b1 = (Beean1)SerUtl.deserialize(s);
Serialize any Serializable object with to a byte array
Yes.
and make a string.
No.
Exact statements see below
os = new ObjectOutputStream(new ByteArrayOutputStream());
os.writeObject(o);
s = os.toString();
// s = Base64.encode(s);//Need this some base 64 impl like Apache ?
s = URLEncoder.encode(s, "UTF-8");
These statements don't even do what you have described, which is in any case incorrect. OutputStream.toString() doesn't turn any bytes into Strings, it just returns a unique object identifier.
Base64 output of 1.
The base64 output should use the byte array as the input, not a String. String is not a container for binary data. See below for corrected code.
ByteArrayOutputStream baos = new ByteArrayOutputStream();
os = new ObjectOutputStream(baos);
os.writeObject(o);
os.close();
s = Base64.encode(baos.toByeArray()); // adjust to suit your API
s = URLEncoder.encode(s, "UTF-8");
This at least accomplishes your objective.
Is it required to base 64 or can skip step 2?
If you want a String you must encode it somehow.
Use java.util.URLEncode.encode to encode the string
This is only necessary if you're sending it as a GET or POST parameter.
Use apache http components or URL class to send from servlet 1 to 2 after naming params
Yes.
On Servlet 2 J2EE framework would have already URLDecoded it, now just do reverse steps and cast to object according to param name.
Yes, but remember to go directly from the base64-encoded string to the byte array, no intermediate String.
Basically looking for the fastest & most convenient way of sending objects between JVMs.
These objectives aren't necessarily reconcilable. The most convenient these days is probably XML or JSON but I doubt that these are faster than Serialization.
os = null;
Setting references that are about to fall out of scope to null is pointless.
HttpParam p = new HttpParam ("bean1", s);
It's possible that HttpParam does the URLEncoding for you. Check this.
You need not convert to string. You can post the binary data straight to the servlet, for example by creating an ObjectOutputStream on top of a HttpUrlConnection's outputstream. Set the request method to POST.
The servlet handling the post can deserialize from an ObjectStream created from the HttpServletRequest's ServletInputStream.
I'd recommend JAXB any time over binary serialization, though. The frameworks are not only great for interoperability, they also speed up development and create more robust solutions.
The advantages I see are way better tooling, type safety, and code generation, keeping your options open so you can call your code from another version or another language, and easier debugging. Don't underestimate the cost of hard to solve bugs caused by accidentally sending the wrong type or doubly escaped data to the servlet. I'd expect the performance benefits to be too small to compensate for this.
Found this Base64 impl that does a lot of the heavy lifting for me : http://iharder.net/base64
Has utility methods :
String encodeObject(java.io.Serializable serializableObject, int options )
Object decodeToObject(String encodedObject, int options, final ClassLoader loader )
Using :
try {
String dat = Base64.encodeObject(srlzblObj, options);
StringBuilder data = new StringBuilder().append("type=");
data.append(appObjTyp).append("&obj=").append(java.net.URLEncoder.encode(dat, "UTF-8"));
Use the type param to tell the receiving JVM what type of object I'm sending. Each servlet/ jsps at most receives 4 types, usually 1. Again since its our own app and classes that we are sending this is quick (as in time to send over the network) and simple.
On the other end unpack it by :
String objData = request.getParameter("obj");
Object obj = Base64.decodeToObject(objData, options, null);
Process it, encode the result, send result back:
reply = Base64.encodeObject(result, options);
out.print("rsp=" + reply);
Calling servlet / jsp gets the result:
if (reply != null && reply.length() > 4) {
String objDataFromServletParam = reply.substring(4);
Object obj = Base64.decodeToObject(objDataFromServletParam, options, null);
options can be 0 or Base64.GZIP
You can use JMS as well.
Apache Active-MQ is one good solution. You will not have to bother with all this conversion.
/**
* #param objectToQueue
* #throws JMSException
*/
public void sendMessage(Serializable objectToQueue) throws JMSException
{
ObjectMessage message = session.createObjectMessage();
message.setObject(objectToQueue);
producerForQueue.send(message);
}
/**
* #param objectToQueue
* #throws JMSException
*/
public Serializable receiveMessage() throws JMSException
{
Message message = consumerForQueue.receive(timeout);
if (message instanceof ObjectMessage)
{
ObjectMessage objMsg = (ObjectMessage) message;
Serializable sobject = objMsg.getObject();
return sobject;
}
return null;
}
My point is do not write custom code for Serialization, iff it can be avoided.
When you use AMQ, all you need to do is make your POJO serializable.
Active-MQ functions take care of serialization.
If you want fast response from AMQ, use vm-transport. It will minimize n/w overhead.
You will automatically get benefits of AMQ features.
I am suggesting this because
You have your own Applications running on network.
You need a mechanism to transfer objects.
You will need a way to monitor it as well.
If you go for custom solution, you might have to solve above things yourselves.

server thread could not see changed fields of a serialized object

I'm facing with a problem about sending and receiving serialized object via TCP sockets. Actually, i can receive/send an object properly between a server thread and client thread.However, the issue is if a changed a property's value of a received/send object ,this change couldn't be realized by the waiting thread. Consider this code sample;
public class ClientThread extends javax.swing.JFrame implements Runnable {
ClientObject mainClient; // Initiliazed after sockets connect to server successfully
.
.
.
String addNewBuddy = JOptionPane.showInputDialog(this, "Enter the Username of the person who you want to add...");
mainClient.setBuddyRequest(true);
mainClient.setBuddyRequestAccount(addNewBuddy);
send.writeObject(mainClient); // write into an ObjectOutputStream
send.flush(); // flush it
System.out.println("mainClient.setBuddyRequest : " + mainClient.isBuddyRequest() + " setBuddyRequestAccount : " + mainClient.getBuddyRequestAccount()); // Check if values changed properly
ClientObject tempClientObject; // temporary an instance of ClientObject
while(( tempClientObject = (ClientObject) receive.readObject()) != null){
if( !tempClientObject.isBuddyRequest() ){
JOptionPane.showMessageDialog(this, "Buddy Request Information", "Requested buddy doesnt exist!!!", JOptionPane.ERROR_MESSAGE);
break;
}
else{
JOptionPane.showMessageDialog(this, "Buddy Request Information", "Requested buddy added into your buddy list succesfully", JOptionPane.INFORMATION_MESSAGE);
labelSetText = tempClientObject.getNickName();
onlineStatus = tempClientObject.isIsOnline();
model.addElement(createPanel());
}
}
.
.
.
}
So after i changed some properties of mainClient i send it to server. Here is the part which server thread waits an object to give some reaction. Moreover, when client sends second object (which makes counter bigger than 0) server thread can read it without errors but i recognize that even client send a modified object as a second message to server there are no differences between properties of first and second object!.
while( ( clientO = (ClientObject) receive.readObject()) != null ){
counterMessage++;
if( counterMessage==1) { //
checkAccountIfExist(toWrite,file.exists(),toModify,clientO); // Check is connected account exist in database of server
} // end of if (counter==1)
else{ // Second time when server waits
// prints counter=2 but clientO.isBuddyRequest printed as 'false'
//instead of 'true' so this makes if statement unreachable!
System.out.println("Counter = " + counterMessage + " BUDDYREQUEST : " + clientO.isBuddyRequest() + " USERNAME : " + clientO.getUserName());
if(clientO.isBuddyRequest()){
System.out.println("Entered");
checkBuddyAvalaible(clientO);
}
}
}
and finally my serializlible ClientObject's code
public class ClientObject implements Serializable {
private static final long serialVersionUID = 8662836292460365873L;
private String userName;
private String password;
private String nickName;
private String message;
private boolean checkAcc;
private LinkedList<ClientObject> buddyList;
private boolean isOnline;
private boolean buddyRequest;
private String buddyRequestAccount;
public ClientObject(String userName, String password){
this.userName = userName;
this.password = password;
this.checkAcc = false;
this.buddyList = new LinkedList<ClientObject>();
this.isOnline = false;
this.buddyRequest = false;
this.buddyRequestAccount = null;
}
...methods of getters and setters
}
I hope i had been clear about the issue and i will appreciated for every answer, well thanks anyway.
All you need to do is call ObjectOutputStream.reset(), or use writeUnshared().
I guess that you are writing the sending code :
.....
mainClient = new ClientObject(userName, password);
String clientNickName = JOptionPane.showInputDialog(this, "Enter your NickName");
mainClient.setNickName(clientNickName);
send.writeObject(mainClient);
send.flush();
......
In a loop. If it is so , You should read this fact about java serialization:
While performing serialization of objects, Java forms a data structure
similar to an Object Graph to determine which objects need to be
serialized. It starts from the main object to serialize, and
recursively traverses all the objects reachable from the main object.
For each object that it encounters, which needs serialization, it
associates an identifier that marks the object as already been
serialized to the given ObjectOutputStream instance. So when Java
encounters the same object that has already been marked as serialized
to the ObjectOutputStream, it does not serialize the object again,
rather a handle to the same object is serialized. This is how Java
avoids having to re-serialize an already serialized object.
EDIT On the basis of EJP's comment I have updated the post to give correct message to the OPs.
After first time you send an object of ClientObject to the OutputStream via ObjectOutputStream, next time when you send the changed object of ClientObject java checks if the object of this type is already being serialized . Since it has already been serialized so , java does'nt serialize the new object created again. And that's why you are getting same object at other side.
The rememdy to this problem is that each time you want to send the changed object of ClientObject reset the ObjectOutputStream like:
send.reset();
And then send the changed object to other end.

Android/Java - ClassNotFoundException for ServerSocket and Socket communication

I'm developing an app where the phone (Android program) is the client trying to send data through a socket to a Java receiver program on my computer.
So far I've been able to send simple strings or whatever, but now I'm trying to send custom objects that I create. I should note that both programs are separate Eclipse projects, and I seem to be having trouble including the same custom class "Order" on the server side (even though I have Order.java in the src folder of the server project).
Some code, for reference:
Server:
private void run() throws Exception {
ServerSocket mySS = new ServerSocket(4443);
while(true) {
Socket SS_accept = mySS.accept();
InputStream is = SS_accept.getInputStream();
ObjectInputStream ois = new ObjectInputStream(is);
Order order = (Order) ois.readObject();
if (order!=null){
java.util.List<String> items = order.getOrders();
int chair = order.getChair();
int table = order.getTable();
double price = order.getPrice();
System.out.println("Table: "+ table + " || Chair: " +chair);
for(String food: items) {
System.out.println(food);
}
System.out.println("Price: $"+price);
}
is.close();
SS_accept.close();
mySS.close();
}
And the relevant part of the client:
try {
mySocket = new Socket(serverService.ipAddress, serverService.port);
os = mySocket.getOutputStream();
oos = new ObjectOutputStream(os);
} catch(Exception e) {
Toast.makeText(PlaceOrder.this, "Error - not connected to server.", Toast.LENGTH_SHORT).show();
}
try {
oos.writeObject(order);
Toast.makeText(PlaceOrder.this, "Order Submitted!", Toast.LENGTH_SHORT).show();
refreshOrderPage(); //refresh page, allow waiter to make more orders
oos.close();
os.close();
mySocket.close();
} catch (Exception e) {
Toast.makeText(PlaceOrder.this, "Error - not connected to server.", Toast.LENGTH_SHORT).show();
}
Any ideas why I'm getting this error when trying to send objects through sockets?
Thanks.
You should probably set the serialVersionUID in the class, then build a jar with the shared classes and include that jar in both projects.
However, given you are using different JVMs (Oracle and Dalvik) there's no guarantee that the byte-level encoding is the same. You should either manually override the serialization using readObject/writeObject or use a different object encoding system that is guaranteed to be identical independent of the environment.
A stack trace would help, but almost certainly you're serializing an object on one side, sending it to the other, and the other side doesn't have a class definition with which it can reconstruct the object. In this case, it sounds like maybe your Server doesn't know about the com.foo.Order class.
You can also serialize object to some string format (json, yaml, xml) and pass it. It would much easier to maintain, I suppose.

Java invalid stream header: 7371007E

I am building a client-server application. Now I want to forward the message from a client to all other client with this code:
ArrayList<User> usrs = _usrHandler.getUsers();
for(User usr : usrs) {
if(!usr.getSocket().equals(_connection)) {
usr._oOut.writeObject(new CommunicationMessage(this._comMsg.getMessage(), CommunicationMessage.MSG,
this._comMsg.getUser()));
}
}
On the client side the program is listening for messages. It throws this exception:
java.io.StreamCorruptedException: invalid stream header: 7371007E
at java.io.ObjectInputStream.readStreamHeader(ObjectInputStream.java:783)
at java.io.ObjectInputStream.<init>(ObjectInputStream.java:280)
at Connection$MessageListener.run(Connection.java:126)
at java.lang.Thread.run(Thread.java:637)
MessageListener:
while(this._loop) {
this._comMsg = (CommunicationMessage) this._dataInput.readObject();
SimpleAttributeSet attr = new SimpleAttributeSet();
attr.addAttribute(StyleConstants.CharacterConstants.Bold, Boolean.TRUE);
attr.addAttribute(StyleConstants.CharacterConstants.Foreground, _comMsg.getUser().getColor());
messageClient.addMessage(_comMsg.getUser().getNickName() + ": ", attr);
messageClient.addMessage(_comMsg.getMessage(), _comMsg.getUser().getColor());
_comMsg = null;
}
Does someone see the error?
You've likely got your streams in a twist.
When you construct an ObjectInputStream, the constructor reads the first two bytes from the stream expecting them to be the "magic values" that should be present in an object stream. If they're not there, it throws the StreamCorruptedException (this is all in the ObjectInputStream source code).
So it would appear that you're wrapper an InputStream in an ObjectInputStream when in fact the data coming down from the other end of the connection is not actually an object stream. Perhaps it's still sending data from a previous communication.

Categories

Resources