Send files over the network using encapsulated serialized class

C

Chris

Hi,

I'm trying to extend a client/server program so that I can send/receive
files. It uses serializable Message objects in order to send messages.
The Message class has a private byte array field which holds the file
being sent. Before sending and receiving a message, the Message object
either gets converted to a byte array or a byte array is converted back
to a Message object. The code for that class:-

import java.io.*;

public class ConvertData{

//Convert a message object into a byte array
public static byte[] messageToBytes (Object object) throws
IOException{
java.io.ByteArrayOutputStream bs = new
java.io.ByteArrayOutputStream();
java.io_ObjectOutputStream out = new java.io_ObjectOutputStream (bs);
out.writeObject(object);
out.flush();
out.close ();
byte [] bytes = bs.toByteArray();
System.out.println("Bytes sending = " + bytes.length);
return bytes;
}

//Convert a byte array into a message object
public static Object bytesToMessage (byte bytes[]) throws IOException,
ClassNotFoundException{


try{
System.out.println("Bytes received = " + bytes.length);
Object object;
java.io_ObjectInputStream in;
java.io.ByteArrayInputStream bs;
bs = new java.io.ByteArrayInputStream (bytes);
in = new java.io_ObjectInputStream(bs);
object = in.readObject();
in.close ();
bs.close ();
return object;
}
catch(StreamCorruptedException sce){
System.out.println("Stream corrupted Exception ");
sce.printStackTrace();
Object o = new Object();
return o;
}
catch(java.lang.ClassCastException cce){
System.out.println("Class Cast Exception ");
cce.printStackTrace();
Object o = new Object();
return o;
}
}
}

I run my own protocol whereby I create the Message object with the file
and obtain the file size. I then send a warning to the server
(ClientThread helper class) to accept a larger file (default is 2048
bytes). The server gets ready to accept the file and sends back an
acknowledgement. The client then sends the file after receiving the
acknowledgement. I've tried various size byte arrays in the Message
object and anything over 1225 bytes I get the following exception
thrown on the server side:-

java.io.StreamCorruptedException
at
java.io_ObjectInputStream.readObject0(ObjectInputStream.java:1326)
at
java.io_ObjectInputStream.defaultReadFields(ObjectInputStream.java:1912)
at
java.io_ObjectInputStream.readSerialData(ObjectInputStream.java:1836)
at
java.io_ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1713)
at
java.io_ObjectInputStream.readObject0(ObjectInputStream.java:1299)
at
java.io_ObjectInputStream.readObject(ObjectInputStream.java:339)
at ConvertData.bytesToMessage(ConvertData.java:39)
at ClientThread.run(Server.java:459)
at java.lang.Thread.run(Thread.java:595)
java.lang.ClassCastException: java.lang.Object

Does anyone have an idea as to what may be the cause?

Kind regards,

Chris
 
R

robert

Chris escreveu:
Hi,

I'm trying to extend a client/server program so that I can send/receive
files. It uses serializable Message objects in order to send messages.
The Message class has a private byte array field which holds the file
being sent. Before sending and receiving a message, the Message object
either gets converted to a byte array or a byte array is converted back
to a Message object. The code for that class:-
<snip>

Why are you writing your own protocol? Using a web service you can use
wsdl type base64Binary which accepts a byte array. Or at least use NIO
if you choose to use sockets. Take a look at ByteBuffer.wrap(byte[]
array) . The example below passes in a String - but could just as
easily be byte [] since in the end you work with ByteBuffer.

private static final Charset ascii = Charset.forName("US-ASCII");
/**
Write data to socket server

@param sChannel Pre-configured, connected Socket
@param send The String to send to socket server
@throws IOException on SocketChannel problems
*/
public static void writeToChannel (SocketChannel sChannel, final
String send) throws IOException
{
CharBuffer cbuf = CharBuffer.wrap(send);
ByteBuffer buf = ascii.encode(cbuf);
while(buf.hasRemaining())
{
sChannel.write(buf);
}
}

HTH,
Robert
http://www.braziloutsource.com/
 
C

Chris

Hi Robert,

I'm going to have to go all newbie on you now, sorry! Basically the
program is for a Chat tool for a team project. By sending the Message
object back and forth between clients and server we have cracked the
chat element, sending user lists, log on/off etc. Ideally we want to
extend the Message class so it encompasses a byte array containing a
file so we don't have to back to square one and start again. The
protocol I mentioned is a protocol in the very loose sense of the word,
just an indication to the server and receiving client that the size of
the file is large therefore up the receiving capacity. As I've said,
byte arrays under 1225 bytes long are ok but that is naff. Just
wondering why it goes to rats at anything over that.

Regards,

Chris
 
C

Chris Uppal

Chris said:
I'm going to have to go all newbie on you now, sorry!

In that case I trust you won't be offended if I suggest you go back to your
sending and recieving code and look for a couple of common errors.

One is using the InputStream.available() method at all. There are /very/ few
valid reasons to use it, and it frequently misleads people.

The other is using InputStream.read(byte[]) (or Reader.read(char[])) without
checking the return value.

-- chris
 
C

Chris

Hi Chris,

InputStream.available()? I'm assuming that you are on about the second
method in the conversion class and perhaps on about in.readObject().
Could you suggest a rehash of the code please. Sorry to be a pain.

Chris
 
C

Chris Uppal

Chris said:
InputStream.available()? I'm assuming that you are on about the second
method in the conversion class and perhaps on about in.readObject().

No, I'm talking about the code you /didn't/ show -- the code that pulled the
byte array off the network.

-- chris
 
C

Chris

OK Chris. I get you now. Right, when the user connects to the server
there are a few authentication checks that take place and on success a
ClientThread is run which listens to subsequent messages from the
client. The code for it is below, if that is any help.

class ClientThread implements Runnable, ApplicationConstants{

private DataInputStream dis;
private Socket socket;
private boolean isDone = false;
private Thread thread;
private User user;
private int uploadSize = MAXIMUM_MESSAGE_SIZE;

public ClientThread(Socket socket, User user){

System.out.println("This is " + user.getUserName() + "'s Thread! and
upload size is " + uploadSize);

try{
//set the socket
this.socket = socket;
//set the user
this.user = user;
//create the input stream to listen for incoming messages
dis = new DataInputStream(socket.getInputStream());
//create the thread
thread = new Thread(this,"ClientThread");
//start the thread
thread.start();
}
catch(Exception e){
System.out.println("service constructor"+e);
}
}

//thread is running and continuously listening for a message from its
client
public void run(){
byte [] data;

//read in the data
while(!isDone){
try{
//set max size of message
data = new byte[uploadSize];
System.out.println("Upload size is " + uploadSize);
//read in the byte array
dis.read(data);
//convert to a message
Message message = (Message)ConvertData.bytesToMessage(data);

//System.out.println("Message header of " +
message.getMessageHeader() + " sent by " +
message.getUser().getUserName());

//Checks if a file is going to be sent. If so, increase the
//size of the byte array to accomodate the file size and the
//message objects.
if(message.getMessageHeader() == FILE_WARNING){
System.out.println("I received a FILE_WARNING");
//increase size of upload
uploadSize = ((int)message.getFileSize());
//acknowledge to sender that server is ready for the file
message.setMessageHeader(FILE_ACKNOWLEDGE);
//output the message back to the sender of the file
Server.outputToSingleClient(message);
}
//file received ok so set array back to default level and pass
//the message to the Server to deal
else if(message.getMessageHeader() == FILE_SENDING){
System.out.println("I received a FILE_SENDING");
//set size of array back to default
uploadSize = MAXIMUM_MESSAGE_SIZE;
//now process the message
Server.processClientMessage(message);
}
//pass to the server for processing
else{
Server.processClientMessage(message);
}
}
//Error in the thread
catch(Exception e){
System.out.println("In the exception block of the thread " + e);

uploadSize = MAXIMUM_MESSAGE_SIZE;
//set isDone to true
isDone = true;
//remove the user from the list
Server.removeFromUserList(user);
//send a CLIENT_LOGOUT message to all other users
Message message = new Message(CLIENT_LOGOUT);
//set the user as this user
message.setUser(user);
//get Server to xend this message to all clients left on the list
Server.outputToAllClients(message);
//try to close the socket
try{
socket.close();
}
catch(Exception se){
System.out.println("Error closing the socket " + se);
}
}
}
}

Just on the client side I have serialized and deserialized the object
without sending it and it remains intact. For clarity as well the
method for sending data to the server is:-

public void sendMessageToServer(Message message) throws
java.io.IOException{

//send details of the user each time
message.setUser(clientUser);
byte [] data;
data = ConvertData.messageToBytes(message);
//create a byte array
//write the data to the socket
dos.write(data,0,data.length);
//flush the data out of the socket
dos.flush();
}

where dos is DataOutputStream.

Cheers,

Chris
 
C

Chris Uppal

Chris said:
The code for it is below, if that is any help.

Sigh...

Actually I suggested that /you/ review your code for the common errors I
mentioned; I didn't offer to find 'em for you !

;-)

But still, I'll give you a hint. You are indeed committing the second of the
them.

-- chris
 
C

Chris

int i;
while(i = dis.read(data) && i != uploadSize){
dis.read(data);
}
or something like that.......?

Sorry, away for the weekend so no chance of trying. Just shows you
though how addictive this Java malarky can be ;-)
 
C

Chris Uppal

Chris said:
int i;
while(i = dis.read(data) && i != uploadSize){
dis.read(data);
}

No. Think about what that would do if (or rather, when) you got a partial read
from the network. Say that you want to read 1000 bytes, and read() actually
supplies 120 in the first chunk.

-- chris
 
C

Chris Uppal

Chris said:
while(dis.read(data) != -1) perhaps?

No. read() is /not/ guaranteed (or even likely in this case) to read as many
bytes as you asked for. (I've just checked back with the documentation and I
admit this is not made very clear there). It will read as many bytes (>= 1) as
it happens to feel like, and then return to your application to tell you how
many you've actually got. If you want to read 1000 bytes then you have to loop
until all 1000 bytes have arrived.

-- chris
 
C

Chris Uppal

Chris said:
*Clutches at straws*

while(dis.read(data)< uploadSize){
dis.read(data);
}

Now you are just thrashing around randomly. Take a deep breath, put down those
straws (they'll only get into the keyboard), and /think/...

And for something to think about, try the three-argument form of read() (URL
will wrap):

http://java.sun.com/j2se/1.5.0/docs/api/java/io/InputStream.html#read(byte[],
int, int)


(BTW, You may think I'm refusing to explain in detail because it would be
better for your education to work through the details. Not a bit of it. I'm
doing it out of sheer sadistic pleasure in tantalising someone who doesn't
follow the Usenet posting conventions of quoting appropriately in replies.)

-- chris
 
C

Chris

Hi sadist, lol! A newbie to Usenet as well I'm afraid.

Right then, part success.

I now have an if else loop here. Basically for any message without a
file the previous code I had written done the trick. I've now added a
boolean value to test if a file is being received. If that is true I
use dis.readFully(data,0,uploadSize) and that works great and the whole
message is received. On receipt of this message I then reverse my
protocol and inform the receiver of the file being sent. The receiver
sends an acknowledgement but the server never receives it. Is there any
chance that the changes in upload size affect each thread?

Another point is that it would be nice to be able to stick to just
dis.readFully etc. To be honest though, I'm not quite sure how it will
work. All messages are of varying length and a warning message,
informing the server of a particular size message can also change in
size by a byte here or there. I was thinking of padding out all sent
messages under 2048 bytes to 2048 bytes so that readFully would work
but would imagine that it would be a real waste of bandwidth. Do you
know of any particular way around it?

Thanks for all your time and patience,

Chris
 
C

Chris

Hi Chris,

Further to the last post I now have the following acting in the thread,
and the whole file transfer thing works great. However if I try to
perform an ordinary chat session, the server hangs. The real thing that
is bugging me is that when I send a file, all control messages get
through (i.e. FILE_WARNING, FILE_ACKNOWLEDGE), so why not CHAT
messages? The code I have is:-

class ClientThread implements Runnable, ApplicationConstants{

private DataInputStream dis;
private Socket socket;
private boolean isDone = false;
private boolean uploadingFile = false;
private Thread thread;
private User user;
private int uploadSize = MAXIMUM_MESSAGE_SIZE;


public ClientThread(Socket socket, User user){

System.out.println("This is " + user.getUserName() + "'s Thread! and
upload size is " + uploadSize);

try{
//set the socket
this.socket = socket;
//set the user
this.user = user;
//create the input stream to listen for incoming messages
dis = new DataInputStream(socket.getInputStream());
//create the thread
thread = new Thread(this,"ClientThread");
//start the thread
thread.start();
}
catch(Exception e){
System.out.println("service constructor"+e);
}
}

//thread is running and continuously listening for a message from its
client
public void run(){
byte [] data;

//read in the data
while(!isDone){
try{
//if an ordinary message is being sent do this
if(!uploadingFile){
System.out.println("Listening for normal messages in " +
user.getUserName() + "'s Thread");
//set max size of message
data = new byte[uploadSize];
System.out.println("Upload size is " + uploadSize);
//read in the byte array
dis.read(data,0,uploadSize);
}
//if a file is being sent then do this
else{
System.out.println("Listening for file messages in " +
user.getUserName() + "'s Thread");
data = new byte[uploadSize];
dis.readFully(data,0,uploadSize);
}

//Convert into a message object
Message message = (Message)ConvertData.bytesToMessage(data);

//Checks if a file is going to be sent. If so, increase the
//size of the byte array to accomodate the file size and the
//message objects.
if(message.getMessageHeader() == FILE_WARNING){
System.out.println("I received a FILE_WARNING");
//increase size of upload
uploadSize = ((int)message.getFileSize());
//set uploadingFile to true
uploadingFile = true;
//acknowledge to sender that server is ready for the file
message.setMessageHeader(FILE_ACKNOWLEDGE);
//output the message back to the sender of the file
Server.outputToSingleClient(message);
}
//file received ok so set array back to default level and pass
//the message to the Server to deal
else if(message.getMessageHeader() == FILE_SENDING){
System.out.println("I received a FILE_SENDING");
//set size of array back to default
uploadSize = MAXIMUM_MESSAGE_SIZE;
//set uploadingFile back to false
uploadingFile = false;
//now process the message
Server.processClientMessage(message);
}
//pass to the server for processing
else{
Server.processClientMessage(message);
}
}
//Error in the thread
catch(Exception e){
System.out.println("In the exception block of the thread " + e);

uploadSize = MAXIMUM_MESSAGE_SIZE;
//set isDone to true
isDone = true;
//remove the user from the list
Server.removeFromUserList(user);
//send a CLIENT_LOGOUT message to all other users
Message message = new Message(CLIENT_LOGOUT);
//set the user as this user
message.setUser(user);
//get Server to xend this message to all clients left on the list
Server.outputToAllClients(message);
//try to close the socket
try{
socket.close();
}
catch(Exception se){
System.out.println("Error closing the socket " + se);
}
}
}
}
}

Any light that can be shed on this will really be appreciated.

Kind regards,

Chris
 
C

Chris

Sorry I'm just being a numpty. My fault that the CHAT message wasn't
being sent. It now does everything I require. Do you think that it
could be made more elegant at all?

Chris
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,537
Members
45,020
Latest member
GenesisGai

Latest Threads

Top