0

I'm trying to send objects from a server to a rendering client, through a socket. The first list of Entity-objects consists of 8 entities, but many more are added to the list. My server can end up writing hundreds of objects to an ObjectOutputStream, but the client only reads the initial 8.

I have tried calling the flush() both before and after the writeObject method, but this does not seem to make a difference. I have narrowed the problem down to be client-sided.

The server writes an ArrayList to the stream using this method:

private void sendEntities() {
    try {
        ArrayList<Entity> entities = engine.requestEntities();
        System.out.println("Entities sent: " + entities.size());
        objectOut.writeObject(entities);
        // objectOut.flush(); Does not fix the problem
    } catch (IOException e) {
        e.printStackTrace();
    }
}

The client reads and returns the objects to a setter-method that draws the them on the screen, but this is the only area that tries to read objects from the stream.

private ArrayList<Entity> requestEntities() {
    try {
        out.writeUTF("REQ_ENT()");
        // The client only receives 8 entity-objects every time
        ArrayList<Entity> entitiesReceived = (ArrayList<Entity>) objectIn.readObject();
        in.readUTF();
        return entitiesReceived;

    } catch (IOException e) {
        e.printStackTrace();
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }

    return null;
}

I expected the list of objects read from the inputstream to match the amount of objects written to the outputstream, but the list read from the inputstream stays the same. It works as expected the first time requestEntities() are called, but not on subsequent calls.

EDIT: a link to the github repository has been added

EDIT 2: The ObjectOutputStream sends a reference to a previously sent object, as pointed out by Johannes Kuhn. I added a line of code that seems to have solved the issue. I make a copy of the entities list, and write this copy to the stream instead.

private void sendEntities() {
    try {
        ArrayList<Entity> entities = engine.requestEntities();
        ArrayList<Entity> copy = new ArrayList<>(entities);
        System.out.println("Entities sent: " + entities.size());
        objectOut.writeObject(copy);
    } catch (IOException e) {
        e.printStackTrace();
    }
}

Link to github repository

brisdalen
  • 210
  • 1
  • 2
  • 10
  • I think you have a bug somewhere outside the code you have shown us. Your expectations about ObjectInputStream/ObjectOutputStream are correct, and there are no obvious problems in the code posted here. – Lars Christian Jensen Aug 04 '19 at 19:30
  • I tried keeping it to the parts that made the trouble, but I can post the github link for further inspection. – brisdalen Aug 04 '19 at 19:41
  • If you keep sending the same list, you will get the same list back, with no changes. Always send a fresh copy. – Johannes Kuhn Aug 04 '19 at 19:49
  • Why are you calling. `readUTF()`, and throwing the result away, when you haven't called `writeUTF()`? – user207421 Aug 04 '19 at 21:00
  • I use writeUTF to request different elements from the server, and I used to use the readUTF for a response (mostly to test the socket communication); but now that you mention it, it seems redundant. – brisdalen Aug 05 '19 at 04:43

1 Answers1

0

The problem is that ObjectStreams keep track of already sent objects, and instead of sending them again, they will just reference the previous object.

Make a copy of your ArrayList and send the new ArrayList.

tl;dr: Serialization was not intended for server/client comunication.

Johannes Kuhn
  • 14,778
  • 4
  • 49
  • 73
  • Oh, I didn't know that, where in the documentation does it say that? Do you reccomend something else beside using serialization for the server/client communication? – brisdalen Aug 04 '19 at 19:56
  • It's probably somewhere in the specification. I either recommend a DataOutputStream or some other protocol. An ObjectInputStream has to keep a reference to all read objects, so you might have some memory leak until you close the OIS. – Johannes Kuhn Aug 04 '19 at 20:04
  • You should have mentioned both `reset()` and `writeUnshared()` as possible solutions. NB Serialization is used for client/server communication in both RMI and JMS. – user207421 Aug 04 '19 at 21:02
  • And both deal with with all those implications. Still, writing clients for RMI or JMS in other languages than Java is hard. I prefer to use JSON, or some other protocol that has bindings to other languages. – Johannes Kuhn Aug 05 '19 at 16:10