0

I have problems with ByteArray and Object streams. I'm creating a multiplayer card game via NIO server. I send the ArrayList private ArrayList<Card> cardsDropped through the method:

public void sendMessage(Object message) {
    if (message instanceof String) {
        messages.add(message + Config.MESSAGE_DELIMITER);
    } else {
        messages.add(message);
    }
    SelectionKey key = channel.keyFor(selector);

    key.interestOps(OP_WRITE);
}

With private final LinkedList<Object> messages that is the list of all objects sent in the stream through -> Client methods:

protected void handleIncomingData(SelectionKey sender, byte[] data) throws IOException {
    ByteArrayInputStream byteObject = new ByteArrayInputStream(data);
    ObjectInputStream obj;
    in = null;

    try {
        obj = new ObjectInputStream(byteObject);   //HERE <--
        byteObject.close();
        in = obj.readObject();
        obj.close();
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }

    handler.onMessage(in);
}

protected void write(SelectionKey key) {
    SocketChannel channel = (SocketChannel) key.channel();

    while (!messages.isEmpty()) {
        ByteArrayOutputStream byteObject = new ByteArrayOutputStream();
        ObjectOutputStream oos = null;

        try {
            oos = new ObjectOutputStream(byteObject);
            oos.writeObject(messages.poll());
            oos.flush();    //TODO ?
            oos.close();
            channel.write(ByteBuffer.wrap(byteObject.toByteArray()));
            byteObject.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    key.interestOps(OP_READ);
}

And Server methods:

protected void write(SelectionKey key) {
    ByteBuffer buffer = (ByteBuffer) key.attachment();
    SocketChannel channel = (SocketChannel) key.channel();

    try {
        channel.write(buffer);
    } catch (IOException e) {
        e.printStackTrace();
    }
}

protected void handleIncomingData(SelectionKey sender, byte[] data) throws IOException {
    for (SelectionKey key : selector.keys()) {
        if (key.channel() instanceof ServerSocketChannel) {
            continue;
        }

        if (key.equals(sender)) {
            continue;
        }

        key.attach(ByteBuffer.wrap(data));
        write(key);
    }
}

After some turns and cards sent, Client thread is sending me some "invalid stream header" here obj = new ObjectInputStream(byteObject);

Is this a problem related to how is the server/client code written? [The code is based on https://github.com/mpkelly/nio-chat ]

Thanks in advance!

  • You don't have any message handling. A TCP socket passes bytes not messages. If you send two messages, the read is only guaranteed to read 1 byte or more. It could read part of one, part of 2 or both messages at once and you have no way of knowing. I suggest you write the length as a 4 byte integer before writing the message. BTW `write` is only guaranteed to write 1 byte as well. It might not write all of the ByteBuffer if the TCP is near full buffer – Peter Lawrey Jun 26 '18 at 12:50
  • BTW If you have a list of Strings you might find it simpler to send them as Strings rather than wrap with with a binary format. – Peter Lawrey Jun 26 '18 at 12:52
  • When you say "length", what do you mean? I'm trying to understand TCP and Server mechanics, because they are new to me (first Server project) so I'm sorry if I'll take longer to understand >.> – Domenico Verde Jun 26 '18 at 13:19
  • https://stackoverflow.com/questions/2840190/java-convert-4-bytes-to-int Is this something related to what you mean? Because it seems similar to your suggestion – Domenico Verde Jun 26 '18 at 13:29
  • It's related though I would use ByteBuffer to write/read the int. – Peter Lawrey Jun 26 '18 at 13:43
  • How should a message handler be structured in this kind of nio server? Sorry for bothering you >.> – Domenico Verde Jun 26 '18 at 16:28
  • you should consider libraries like vert.x for handling nio buffers. – gagan singh Jun 26 '18 at 19:05
  • The way we do it is to write a length first and always send he data in a loop until it is all sent or an error occurs. When reading you expect to read at least 4 bytes as the length, keep reading until you do and then keep reading until you have the length you need. This way you know you have exactly one message. To improve efficiency you can allow for batch multiple message on write and read but you will want to do other optimisations first before getting to that level. – Peter Lawrey Jun 27 '18 at 10:08

0 Answers0