java - Kryonet RMI,无法等待连接更新线程的响应

标签 java network-programming rmi kryonet

我想使用 LibGDX 和 Kryonet 库,使用 RMI 制作一个游戏。所以我创建了干净的项目。我现在想做的是,设置服务器监听端口 10048 并在新连接上打印客户端的名称,我将通过调用客户端类上的方法来获取该名称...

这是代码:

ICardsTableImpl.java

package clzola.cardstable.client;

public interface ICardsTableGameImpl {
    public String getName();
}

CardsTableServer.java

package clzola.cardstable.server;

import clzola.cardstable.client.ICardsTableGameImpl;
import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryonet.Connection;
import com.esotericsoftware.kryonet.Server;
import com.esotericsoftware.kryonet.rmi.ObjectSpace;
import com.esotericsoftware.minlog.Log;

import java.io.IOException;
import java.util.HashMap;


public class CardsTableServer extends Server {
    private HashMap<Integer, Connection> connections;

    public CardsTableServer() throws IOException {
        connections = new HashMap<Integer, Connection>();
        addListener(new NetworkListener(this));

        Kryo kryo = getKryo();
        ObjectSpace.registerClasses(kryo);
        kryo.register(ICardsTableGameImpl.class);

        bind(10048);
    }

    @Override
    protected Connection newConnection() {
        Player player = new Player();
        addConnection(player);
        return player;
    }


    public void addConnection(Connection connection) {
        this.connections.put(connection.getID(), connection);
    }

    public Connection getConnection(int connectionId) {
        return this.connections.get(connectionId);
    }

    public Connection removeConnection(int connectionId) {
        return this.connections.remove(connectionId);
    }

    public static void main(String[] args) throws IOException {
        Log.set(Log.LEVEL_DEBUG);
        CardsTableServer server = new CardsTableServer();
        server.start();
    }
}

NetworkListener.java

package clzola.cardstable.server;

import clzola.cardstable.client.ICardsTableGameImpl;
import com.badlogic.gdx.Gdx;
import com.esotericsoftware.kryonet.Connection;
import com.esotericsoftware.kryonet.Listener;
import com.esotericsoftware.kryonet.rmi.ObjectSpace;

public class NetworkListener extends Listener {
    private CardsTableServer server;

    public NetworkListener(CardsTableServer server) {
        this.server = server;
    }

    @Override
    public void connected(Connection connection) {
        Player player = ((Player) connection);

        ICardsTableGameImpl game = ObjectSpace.getRemoteObject(player, 0, ICardsTableGameImpl.class);
        player.name = game.getName(); // This is where I get excpetion...

        Gdx.app.log("Server", "Player name: " + player.name);
    }

    @Override
    public void disconnected(Connection connection) {
        server.removeConnection(connection.getID());
    }
}

Player.java

package clzola.cardstable.server;

import com.esotericsoftware.kryonet.Connection;

public class Player extends Connection {
    public String name;
}

CardsTableGame.java

package clzola.cardstable.client;

import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.utils.viewport.ScreenViewport;
import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryonet.Client;
import com.esotericsoftware.kryonet.rmi.ObjectSpace;

public class CardsTableGame extends ApplicationAdapter implements ICardsTableGameImpl {
    SpriteBatch batch;
    Stage stage;
    Client client;
    String name = "Lazar";
    ObjectSpace objectSpace;

    @Override
    public void create () {
        batch = new SpriteBatch();
        stage = new Stage(new ScreenViewport(), batch);

        try {
            client = new Client();
            client.start();

            Kryo kryo = client.getKryo();
            ObjectSpace.registerClasses(kryo);
            kryo.register(ICardsTableGameImpl.class);

            ObjectSpace objectSpace = new ObjectSpace();
            objectSpace.register(0, this);
            objectSpace.addConnection(client);

            client.connect(5000, "127.0.0.1", 10048);
        } catch (Exception e) {
            Gdx.app.log("CardsTableGame", e.getMessage(), e);
        }
    }

    @Override
    public void render () {
        Gdx.gl.glClearColor(0, 0, 0, 1);
        Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
    }

    @Override
    public String getName() {
        return this.name;
    }
}

运行后,我在服务器端遇到异常:

Exception in thread "Server" java.lang.IllegalStateException: Cannot wait for an RMI response on the connection's update thread.
    at com.esotericsoftware.kryonet.rmi.ObjectSpace$RemoteInvocationHandler.waitForResponse(ObjectSpace.java:420)
    at com.esotericsoftware.kryonet.rmi.ObjectSpace$RemoteInvocationHandler.invoke(ObjectSpace.java:408)
    at com.sun.proxy.$Proxy0.getName(Unknown Source)
    at clzola.cardstable.server.NetworkListener.connected(NetworkListener.java:24)
    at com.esotericsoftware.kryonet.Server$1.connected(Server.java:48)
    at com.esotericsoftware.kryonet.Connection.notifyConnected(Connection.java:214)
    at com.esotericsoftware.kryonet.Server.acceptOperation(Server.java:417)
    at com.esotericsoftware.kryonet.Server.update(Server.java:249)
    at com.esotericsoftware.kryonet.Server.run(Server.java:372)
    at java.lang.Thread.run(Thread.java:745)

我不知道为什么......我做错了什么? (这是我第一次尝试使用 RMI)

最佳答案

监听器由 Kryonet-update-thread 执行。该线程定期检查套接字以接收消息。调用 game.getName() 会让调用者等待,直到答案通过网络传递。如果你在更新线程上这样做,你可能会让你的服务器陷入死锁,因为 kryonet 无法收到它正在等待的答案,因为你阻塞了更新线程。这就是它抛出异常的原因。

rmi example中从 kryonet git 他们通过使用在自己的线程上工作的监听器解决了这个问题。

// The ThreadedListener means the network thread won't be blocked when waiting for RMI responses.
    client.addListener(new ThreadedListener(new Listener() {
        public void connected (final Connection connection) {
            TestObject test = ObjectSpace.getRemoteObject(connection, 42, TestObject.class);
            // Normal remote method call.
            assertEquals(43.21f, test.other());
            // Make a remote method call that returns another remote proxy object.
            OtherObject otherObject = test.getOtherObject();
            // Normal remote method call on the second object.
            assertEquals(12.34f, otherObject.value());
            // When a remote proxy object is sent, the other side recieves its actual remote object.
            connection.sendTCP(otherObject);
        }
    }));

关于java - Kryonet RMI,无法等待连接更新线程的响应,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34845390/

相关文章:

java - Hashmap get 返回 null

java - 将对象命名为与类名相同但没有大写字母是否违反 Java 约定?

java - 通过 TCP/IP 的 RS485 通信

java - 不使用系统属性的带 SSL 的 JMX

java - 创建简单 RMI 应用程序时出现 "ClassCastException: $Proxy0 cannot be cast"错误

java - 无法使用 Spring 进行 JUnit 测试

java - 前台事件未在 Vugen(HP Load Runner) 中使用 Java 记录重放协议(protocol)进行记录

cocoa - Cocoa中用于检测机器的框架/函数是什么

c# - 如何获取 3G 调制解调器的 IP 地址?

Java RMI : stopping a Thread running on the client machine