Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
HomeAnnouncementsWhite Papers
Discussion GroupsFirst AidDatabasesJavaBeansGUIJava 3DVirtual MachineCORBASecurityToolsGeneral
Java DirectoryOpen Source ProjectsSample Book ChaptersUser GroupsWeb Resources
Related Topics
Databases.NETMore Topics ...

Java Forum / General / February 2006

Tip: Looking for answers? Try searching our database.

Send files over the network using encapsulated serialized class

Thread view: 
Chris - 23 Feb 2006 15:08 GMT
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
robert - 23 Feb 2006 17:10 GMT
> Hi,
>
[quoted text clipped - 4 lines]
> 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/
Chris - 23 Feb 2006 19:01 GMT
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
Chris Uppal - 24 Feb 2006 11:23 GMT
> 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
Chris - 24 Feb 2006 13:12 GMT
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
Chris Uppal - 24 Feb 2006 13:42 GMT
> 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
Chris - 24 Feb 2006 15:21 GMT
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
Chris Uppal - 25 Feb 2006 13:15 GMT
> 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
Chris - 25 Feb 2006 23:00 GMT
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 ;-)
Chris Uppal - 26 Feb 2006 10:50 GMT
> 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
Chris - 27 Feb 2006 07:47 GMT
while(dis.read(data) != -1) perhaps?
Chris Uppal - 27 Feb 2006 09:01 GMT
> 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
Chris - 27 Feb 2006 09:32 GMT
*Clutches at straws*

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

LOL
Chris Uppal - 27 Feb 2006 10:48 GMT
> *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
Chris - 27 Feb 2006 20:40 GMT
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
Chris - 27 Feb 2006 21:47 GMT
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
Chris - 28 Feb 2006 09:11 GMT
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


Free Magazines

Get these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...

Oracle MagazineNetwork ComputingComputer WorldBio-IT WorldeWeekInformation WeekInfosecurity
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2009 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.