Java serializable对象中的ArrayList未进行写共享/读非共享(通过网络)

Java serializable对象中的ArrayList未进行写共享/读非共享(通过网络),java,networking,arraylist,Java,Networking,Arraylist,我正在开发一个基于我自己的客户机-服务器体系结构的Java多人游戏。简言之,客户端每秒30次请求服务器的世界对象的副本,并在收到副本后将其客户端副本设置为响应。这一切都是使用Java的标准NetAPI完成的 我遇到的问题是,我还存储了世界上玩家对象的ArrayList,当我将一个玩家添加到此列表时,客户端不会得到更新。它仍然从服务器接收世界的副本,但它不是最新的 在过去的一个项目中,我遇到了一个类似的问题,它是由write/readObject引起的,并且使用write/readUnshared

我正在开发一个基于我自己的客户机-服务器体系结构的Java多人游戏。简言之,客户端每秒30次请求服务器的世界对象的副本,并在收到副本后将其客户端副本设置为响应。这一切都是使用Java的标准NetAPI完成的

我遇到的问题是,我还存储了世界上玩家对象的ArrayList,当我将一个玩家添加到此列表时,客户端不会得到更新。它仍然从服务器接收世界的副本,但它不是最新的

在过去的一个项目中,我遇到了一个类似的问题,它是由write/readObject引起的,并且使用write/readUnshared修复了它,但即使这样也不起作用。

以下是通信服务器端的重要信息:

String message;
int sum = 0;
while(active)
{
    message = "";
    try {
        message = in.readUTF();
    } catch (IOException e) {
        active = false;
        System.out.println("Lost connection with client " + socket.getInetAddress());
    }

    if(message.equals("GETWORLD"))
    {
        try {
            sum++;
            if(sum == 100)
                main.world.addPlayer(999, 2, 2);
            System.out.println("Client requested world (#" + sum + ")");
            System.out.println(main.world.players.size());
            out.writeUnshared(main.world);
            out.flush();
            System.out.println("Sent client world (#" + sum + ")");
        } catch (IOException e) {
            active = false;
            System.out.println("Lost connection with client " + socket.getInetAddress());
        }
    }

    if(message.equals("DISCONNECT"))
    {
        active = false;
        System.out.println("Client " + socket.getInetAddress() + " requested disconnect");
    }
}
然后是客户端:

Object read = null;
int sum = 0;
while(active)
{
    try {
        Thread.sleep((long)(1000 / 30.0));

        if(connected)
        {
            sum++;
            System.out.println("Asking server for world (#" + sum + ")");
            out.writeUTF("GETWORLD");
            out.flush();

            read = in.readUnshared();
            if(read instanceof World)
            {
                World temp = (World)read;
                System.out.println(temp.players.size());
                frame.panel.updateWorld((World)read);
                System.out.println("Got world from server (#" + sum + ")");
            }
        }
    } catch (InterruptedException | ClassNotFoundException e1) {
        active = false;
        e1.printStackTrace();
    } catch (IOException e2) {
        active = false;
        System.out.println("Lost connection with server @ " + socket.getInetAddress());
        frame.dispose();
        System.exit(0);
    }
}
显然,sum变量用于调试。 我用一些输出进一步测试了这一点,以下是令我害怕的:

Client log:
...
Asking server for world (#99)
1
Got world from server (#99)
Asking server for world (#100)
1
Got world from server (#100)
Asking server for world (#101)
1
Got world from server (#101)
...

Server log:
...
Client requested world (#99)
1
Sent client world (#99)
Client requested world (#100)
2
Sent client world (#100)
Client requested world (#101)
2
Sent client world (#101)
...
您可以在这里看到,即使请求编号匹配,世界对象中玩家对象的数量之间也存在明显的差异

以下是来自世界和玩家职业的重要信息,供好奇者参考:

public class World implements Serializable
{

    public ArrayList<Room> rooms;
    public ArrayList<Player> players;

    private QuickMaths qm;

我很抱歉,如果这是一个长期而容易的问题。我不确定这是引用问题还是其他网络怪癖,但这真的让我发疯了。提前感谢。

您的问题在于书面共享,这有点误导。 阅读:

“请注意,上述规则仅适用于基本级别 对象以writeUnshared写入,而不是以传递方式写入 要序列化的对象图中引用的子对象。“

这意味着不会写入播放器对象两次,但将使用序列化树中对该对象的旧引用

解决方法是在每次写入调用后调用该方法,以确保旧的写入对象不会再次被引用

因此:


我正要给你写回信,突然它关闭了。你的问题是写作不共享。子对象仍然是共享的,因此不会重写播放器。每次写入后使用重置方法写入一个新对象。难以置信!-我还没有在任何地方看到记录!!我会试着这样做,看看是否有效。非常感谢。编辑:事后使用reset()实际上是可行的。问题解决了。啊哈-我希望他们不要关闭这个。。。。无论如何,请参见:“请注意,上述规则仅适用于使用writeUnshared编写的基本级别对象,而不适用于要序列化的对象图中的任何传递引用子对象。”@JB Nizet我认为应该重新打开此规则。虽然解决方案大致相同,但这里的用户依赖于writeUnshared,这往往会产生误导。也许标题应该被编辑,但我不认为这个问题应该结束。不管怎样,你的电话。@zipo13我重新开始了这个问题。
public class Player implements Serializable
{

    private double xPos;
    private double yPos;
    private Color color;

    int id;
    ...
out.writeUnshared(main.world);
out.flush();
out.reset();