javajunitkryonet

Message not being received by kryonet listener?


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;
        }
    }
}

Solution

  • 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.