2

So I'm having a problem testing my networking, there's a lot of code below but this is as small as I could make an example. The problem I'm having is that it seems the listener I'm registering with the server and client never gets called when when I send a message.

lock.await(5000, TimeUnit.MILLISECONDS) should wait until the message is received or timeout at 5 seconds. However the listener should tell the lock to continue lock.countDown() after it sets the response variable that prevents the test from failing.

As you can guess, this doesn't happen, the message gets sent, the lock continues due to timeout and the test fails because response is null.

import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryonet.Client;
import com.esotericsoftware.kryonet.Connection;
import com.esotericsoftware.kryonet.Listener;
import com.esotericsoftware.kryonet.Server;
import com.esotericsoftware.minlog.Log;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

import java.io.IOException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

import static junit.framework.TestCase.fail;

public class TestMessage {
    private Server server;
    private Client client;
    private String response;
    private CountDownLatch lock = new CountDownLatch(1);

    @Before
    public void setUp() {
        Log.set(Log.LEVEL_DEBUG);

        server = new Server();
        try {
            Log.debug("Binding to port: " + 30454 + "... ");
            server.bind(30454);
            Log.debug("Bound to port" + 30454);
        } catch (IOException e) {
            Log.debug("failed");
            Log.error("Unable to bind to port: " + e.getMessage());
            server.stop();
            fail("Unable to start server");
        }
        Kryo kryo = server.getKryo();
        kryo.register(Message.class);
        Log.debug("Adding server listener");
        server.addListener(new TestListener());
        Log.debug("Starting server... ");
        server.start();
        Log.debug("Server started successfully");
    }

    @Test
    public void testPacketSending() throws IOException, InterruptedException {
        client = new Client();
        client.addListener(new TestListener());
        Kryo kryo = client.getKryo();
        kryo.register(Message.class);
        client.start();
        client.connect(5000, "127.0.0.1", 30454);

        client.sendTCP(new Message("RECEIVED"));

        lock.await(5000, TimeUnit.MILLISECONDS);

        Assert.assertNotNull(response);
    }

    private class TestListener extends Listener {
        @Override
        public void received(Connection connection, Object o) {
            Message m = (Message) o;
            response = m.message;
            Log.debug(m.message);
            lock.countDown();
        }
    }

    private class Message {
        String message;

        Message(String message) {
            this.message = message;
        }
    }
}
Renari
  • 822
  • 7
  • 16
  • 32

1 Answers1

1

The issue here is this:

00:00 ERROR: [kryonet] Error reading TCP from connection: Connection 1
com.esotericsoftware.kryonet.KryoNetException: Error during deserialization.
    at com.esotericsoftware.kryonet.TcpConnection.readObject(TcpConnection.java:141)
    at com.esotericsoftware.kryonet.Server.update(Server.java:205)
    at com.esotericsoftware.kryonet.Server.run(Server.java:372)
    at java.lang.Thread.run(Thread.java:745)
Caused by: com.esotericsoftware.kryo.KryoException: Class cannot be created (non-static member class): org.glytching.sandbox.kryo.TestMessage$MessageA
    at com.esotericsoftware.kryo.Kryo$DefaultInstantiatorStrategy.newInstantiatorOf(Kryo.java:1308)
    at com.esotericsoftware.kryo.Kryo.newInstantiator(Kryo.java:1127)
    at com.esotericsoftware.kryo.Kryo.newInstance(Kryo.java:1136)
    at com.esotericsoftware.kryo.serializers.FieldSerializer.create(FieldSerializer.java:562)
    at com.esotericsoftware.kryo.serializers.FieldSerializer.read(FieldSerializer.java:538)
    at com.esotericsoftware.kryo.Kryo.readClassAndObject(Kryo.java:816)
    at com.esotericsoftware.kryonet.KryoSerialization.read(KryoSerialization.java:55)
    at com.esotericsoftware.kryonet.TcpConnection.readObject(TcpConnection.java:139)
    ... 3 more

Kryonet cannot deserialize into Message because (a) it is a non static inner class and (b) it has no public zero-arg constructor. FWIW, there is some background here on why Kryonet does not support serialisation of non static inner classes.

If you (a) refactor Message into its own class or make it static and (b) give it a zero-arg constructor then your test will pass.

The smallest change necessary to make your test pass is to replace this ...

private class Message {
    String message;

    Message(String message) {
        this.message = message;
    }
}

... with this:

private static class Message {
    String message;

    Message() {

    }

    Message(String message) {
        this.message = message;
    }
}

Re this:

there's a lot of code below but this is as small as I could make an example

The reproduction case in your question was more than adequate, thanks.

glytching
  • 44,936
  • 9
  • 114
  • 120
  • Ah, so all Kryonet message classes need to be static? – Renari Nov 21 '17 at 21:38
  • Only if they are **inner** classes. – glytching Nov 21 '17 at 21:40
  • Hm, thanks. It seems I've failed to reproduce my issue then since only this example uses inner classes. Maybe it's because my class doesn't have a 0 arg constructor then. How did you get this error output? When I set ```Log.set(Log.LEVEL_ERROR)``` I don't see this output to my console. – Renari Nov 21 '17 at 21:48
  • I just ran the test using the code you provided. I made no changes to what you provided. Then, when the issue manifested itself I added the static modified and the zero and constructor and then the test passed. – glytching Nov 21 '17 at 21:55
  • I fixed the actual issue, it was because none of my classes had 0 arg constructors. – Renari Nov 21 '17 at 22:07