Paul
Paul

Reputation: 266

Errors when reading from ObjectInputStream Java

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!

Upvotes: 1

Views: 7191

Answers (3)

Paul
Paul

Reputation: 266

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.

Upvotes: 3

user207421
user207421

Reputation: 311052

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.

Upvotes: 1

Thomas W
Thomas W

Reputation: 14154

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.

Upvotes: 2

Related Questions