1

I am trying to write an implementation of RemoteDesktop in Java. I am using an ObjectOutputStream and ObjectInputStream over a socket to send the data. To send the data, I am using a class I created called "Packet":

import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import javax.swing.ImageIcon;

/**
 * Holds data to send over network connection
 */
class Packet<T extends Serializable> implements Serializable {

    private T payload;

    public Packet() {
        super();
    }

    public Packet(T data) {
        super();
        setPayload(data);
    }

    public T getPayload() {
        return payload;
    }

    public void setPayload(T payload) {
        this.payload = payload;
    }

    public static void send(String message, ObjectOutputStream out) throws IOException {
        out.writeObject(new Packet<>(message));
        out.flush();
    }

    public static void send(Integer value, ObjectOutputStream out) throws IOException {
        out.writeObject(new Packet<>(value));
        out.flush();
    }

    public static void send(Block block, ObjectOutputStream out) throws IOException {
        out.writeObject(new Packet<>(block));
        out.flush();
    }

    public static void send(BufferedImage[][] images, ObjectOutputStream out) throws IOException {
        //convert to ImageIcon
        ImageIcon icons[][] = new ImageIcon[images.length][images[0].length];
        for (int x = 0; x < images.length; x++) {
            for (int y = 0; y < images[0].length; y++) {
                icons[x][y] = new ImageIcon(images[x][y]);
            }
        }
        out.writeObject(new Packet<>(icons));
        out.flush();
    }
}

When the connection is first established, the server sends a 2-D ImageIcon array that contains different "blocks" of the screen. The screen is divided into this array of blocks. The server then takes regular screenshots and compares each "block" of the screen to the last one to see if it has changed. If there is a change, then the server will send the new screen area in a class called "Block", which holds the x and y coordinates of this block and the ImageIcon:

import java.awt.image.BufferedImage;
import java.io.Serializable;
import javax.swing.ImageIcon;

/**
 * Holds an image and its x and y coordinates on the screen
 */
class Block implements Serializable {

    private ImageIcon img;
    private int x;
    private int y;

    public Block() {
        super();
    }

    public Block(BufferedImage image, int x, int y) {
        img = new ImageIcon(image);
        this.x = x;
        this.y = y;
    }

    public Block(ImageIcon image, int x, int y) {
        img = image;
        this.x = x;
        this.y = y;
    }

    public int getx() {
        return x;
    }

    public int gety() {
        return y;
    }

    public ImageIcon getImage() {
        return img;
    }
}

Here is the main code for the server:

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class Server {

    public static void main(String args[]) throws IOException {

        ServerSocket s = new ServerSocket(5000);
        do {
            Socket c;
            ObjectOutputStream out;
            ObjectInputStream in;

            //start listening
            echo("Listening on port: 5000");

            //accept connection
            c = s.accept();
            echo("Connected to client at address " + c.getInetAddress().getHostAddress());

            //open IO streams
            out = new ObjectOutputStream(c.getOutputStream());
            in = new ObjectInputStream(c.getInputStream());

            ServerSession rdsession = new ServerSession(in, out); //start session
        } while (true);
    }

    private static void err(String message) {
        //prints error message and exits
        System.err.println(message);
        System.exit(1);
    }

    private static void echo(String message) {
        //prints message
        System.out.println(message);
    }
}

So, when the connection is established, the server creates an instance of the class "ServerSession". This class handles the RD session by determining when to send new images in a "Block" object. When it needs to update a block to the client, it uses the Packet.send(Block, ObjectOutputStream) method.

The object reads objects from the stream using this code:

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import javax.swing.ImageIcon;

public class Client {

    public static void main(String[] args) throws IOException, ClassNotFoundException {
        Socket c;
        ObjectOutputStream out;
        ObjectInputStream in;
        String serverIP = "127.0.0.1"; //replace with server IP
        int port = 5000;

        //open connection and streams
        c = new Socket(serverIP, port);
        out = new ObjectOutputStream(c.getOutputStream());
        in = new ObjectInputStream(c.getInputStream());

        ClientSession cs = null;

        do {
                Packet<?> p;
                Object o = in.readObject();
                if (o instanceof Packet) {
                    p = (Packet<?>) o;
                } else {
                    continue;
                }
                if (p.getPayload() instanceof String) { //check if string
                    echo("Server>" + p.getPayload());
                } else if (p.getPayload() instanceof Block) { //check if block
                    Block b = (Block) p.getPayload();
                    if (cs != null) {
                        cs.setImage(b.getImage(), b.getx(), b.gety());
                    }
                } else if (p.getPayload() instanceof ImageIcon[][]) { //check if 2-D image array
                    cs = new ClientSession(in, out, (ImageIcon[][]) p.getPayload()); //create session with image array
                }
        } while (true);
    }

    private static void err(String message) {
        System.err.println(message);
        System.exit(1);
    }

    private static void echo(String message) {
        System.out.println(message);
    }
}

The ClientSession class simply stores the screen images and handles the GUI.

When I run the server and client together, I get a myriad of errors after the initial ImageIcon[][] array is sent, each time that the client tries to read a "Block" instance. These are the errors that are regularly thrown:

1:

Exception in thread "main" java.io.InvalidClassException: Block; invalid descriptor for field 
    at java.io.ObjectStreamClass.readNonProxy(ObjectStreamClass.java:710)
    at java.io.ObjectInputStream.readClassDescriptor(ObjectInputStream.java:828)
    at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1599)
    at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1515)
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1769)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1348)
    at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:1989)
    at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1913)
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1796)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1348)
    at java.io.ObjectInputStream.readObject(ObjectInputStream.java:370)
    at Client.main(Client.java:46)
Caused by: java.lang.IllegalArgumentException: illegal signature
    at java.io.ObjectStreamField.<init>(ObjectStreamField.java:122)
    at java.io.ObjectStreamClass.readNonProxy(ObjectStreamClass.java:708)
    ... 11 more

2:

Exception in thread "main" java.io.InvalidClassException: Block; invalid descriptor for field sq ~ sq
    at java.io.ObjectStreamClass.readNonProxy(ObjectStreamClass.java:710)
    at java.io.ObjectInputStream.readClassDescriptor(ObjectInputStream.java:828)
    at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1599)
    at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1515)
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1769)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1348)
    at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:1989)
    at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1913)
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1796)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1348)
    at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:1989)
    at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1913)
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1796)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1348)
    at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:1989)
    at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1913)
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1796)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1348)
    at java.io.ObjectInputStream.readObject(ObjectInputStream.java:370)
    at Client.main(Client.java:25)
Caused by: java.lang.IllegalArgumentException: illegal signature
    at java.io.ObjectStreamField.<init>(ObjectStreamField.java:122)
    at java.io.ObjectStreamClass.readNonProxy(ObjectStreamClass.java:708)
    ... 19 more

3:

Exception in thread "main" java.io.EOFException
    at java.io.DataInputStream.readInt(DataInputStream.java:392)
    at java.io.ObjectInputStream$BlockDataInputStream.readInt(ObjectInputStream.java:2818)
    at java.io.ObjectInputStream.readInt(ObjectInputStream.java:969)
    at javax.swing.ImageIcon.readObject(ImageIcon.java:481)
    at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:1017)
    at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1891)
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1796)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1348)
    at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:1989)
    at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1913)
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1796)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1348)
    at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:1989)
    at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1913)
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1796)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1348)
    at java.io.ObjectInputStream.readObject(ObjectInputStream.java:370)
    at Client.main(Client.java:25)

4:

Exception in thread "main" java.io.StreamCorruptedException: invalid type code: 73
    at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1518)
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1769)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1348)
    at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:1989)
    at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1913)
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1796)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1348)
    at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:1989)
    at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1913)
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1796)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1348)
    at java.io.ObjectInputStream.readObject(ObjectInputStream.java:370)
    at Client.main(Client.java:25)

Thank you for taking the time to read this post. Any advice would be greatly appreciated. Thanks!

8
  • 1
    Your question is very long. Could you please trim it down to one problem? Commented Jul 28, 2013 at 19:41
  • 2
    Posting a SSCCE would help: sscce.org Commented Jul 28, 2013 at 19:42
  • what does clientsession do with the input and output streams? Commented Jul 28, 2013 at 20:04
  • @jtahlborn Nothing right now, the constructor doesn't even use them. They are just there for the future to send mouse x and y to the server. Commented Jul 28, 2013 at 22:10
  • 1
    does the serialized object contain an inner class? this can be one cause, if the VMs are different. Commented Jul 29, 2013 at 0:34

3 Answers 3

3

After spending a lot of time going through my code, I determined that the issue was related to the way in which I implemented multi-threading on the server. Threads would be attempting to write to the output stream simultaneously, which led to the stream becoming corrupted. Thank you all for your input.

Sign up to request clarification or add additional context in comments.

Comments

2

Your serialization & data transport/exchange have fallen out of sync -- the Client is no longer aligned and reading, where the Server sent (serialized) valid object starts.

That's presumably why you get InvalidClassException: Block; invalid descriptor for field and StreamCorruptedException.

Your data transport code mostly depends on serialization & ImageIcon.

Not that this approach is wrong, but there are many assumptions necessary for this to work:

  1. class versions are exactly identical,
  2. SerialVersion UIDs match,
  3. ImageIcon is serializable,
  4. ImageIcon internal data is compatible between platforms,
  5. serialization & litte/big endian formats are compatible.

Try stripping out parts (like the ImageIcon stuff) and see if you can keep the link synchronized & in good order. Once you've identified what caused the link to become de-synchronized, you can investigate why.

3 Comments

(1) is not necessary. (3) is stated in the Javadoc. (5) is guaranteed by the Object Serialization Specification. I agree with your suggestion about getting out of sync but I don't see anywhere in the code where that could happen.
Haha, I agree ;) However, until proven otherwise I prefer to keep a fully broad-based view of the problem. It should help his understanding & investigation, to know all the factors he is depending on..
(5) at inner classes the ser spec are (or were) not perfect. diff VMs behave(d) differently
1

There is something wrong with your Block.class file. I would recompile , re-deploy to both ends, and re-test.

EOFException is to be expected: you tried to read past the end of the ObjectInputStream, i.e. when the peer had closed his end of the connection. You need to catch that separately to get out of your do/while (true).

NB You should create the server-side ObjectOutputStream and ObjectInputStream in the run() method of the started thread. At present you are creating them inside the accept() loop, and as both perform I/O that can block the accept thread from accepting other clients.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.