【问题标题】:Transferring Object over Sockets-Serializable通过套接字传输对象 - 可序列化
【发布时间】:2019-07-09 00:27:43
【问题描述】:

这是我编写的用于通过套接字传输包含任何对象 vm(属于 VM 类)的对象 pm(属于 PM 类)的代码。

public class PM implements Serializable{
    private static final long serialVersionUID=1L;

    VM vm[]=new VM[10];

    //IP of the Agent
    String IP;

    public PM() {
        super();
        for(int i=0;i<10;i++){
            vm[i]=new VM();}
        }
    }

VM 是另一个有自己属性的类。

public class VM implements Serializable{
       String osType;
}

通过套接字交换对象 pm 发生在 2 台 PC 之间。在服务器执行网络发现后,服务器端从客户端接收对象(因此有类名)。

public class NetworkDiscovery extends TimerTask {
    InetAddress controllerIP;
    int controllerPort;
    static PM pm = new PM();

    NetworkDiscovery() throws UnknownHostException {
        controllerIP=InetAddress.getLocalHost();
        controllerPort=4455;
    }
    public void run() {
        try {
            byte[] recvBuf = new byte[5000];
            DatagramPacket packet = new DatagramPacket(recvBuf, recvBuf.length);
            DatagramSocket dSock = new DatagramSocket(4445);
            dSock.receive(packet);
            int byteCount = packet.getLength();
            ByteArrayInputStream byteStream = new ByteArrayInputStream(recvBuf);
            ObjectInputStream is = new ObjectInputStream(new BufferedInputStream(byteStream));
            pm=(PM)is.readObject();
            System.out.println("object1" +pm.IP);
            is.close();
            dSock.close();
            System.out.println("object" + pm.vm[0].vmName);
        } //exceptions are caught etc.
    }
}

在客户端将 pm 对象发送到服务器:

public class ackDiscovery extends TimerTask{
    int agentListenPort;
    InetAddress agentIP;
    ackDiscovery(Connect c) {
        agentListenPort=4445;
        c1=c;
    }
    public void run() {
        ObjectOutputStream os = null;
        try {
            PM pm = new PM();
            {
                pm.IP = InetAddress.getLocalHost().toString();
                pm.vm[i].osType = d1.getOSType();
                System.out.println("VMname" +i +pm.vm[i].osType);
                pm.vm[i].status = d1.isActive();
            }
            InetAddress address = InetAddress.getByName("Server_IP");
            ByteArrayOutputStream byteStream = new ByteArrayOutputStream(15000);
            os = new ObjectOutputStream(new BufferedOutputStream(byteStream));
            os.flush();
            os.writeObject((PM) pm);
            os.flush();
            byte[] sendBuf = byteStream.toByteArray();
            DatagramPacket packet = new DatagramPacket(sendBuf, sendBuf.length, address, 4445);
            int byteCount = packet.getLength();
            DatagramSocket dSock = new DatagramSocket();
            System.out.println("Quote of the Moment: " + pm.vm[0].osType);
            dSock.send(packet);
            os.close();
            dSock.close();
        } //exceptions caught etc.
    }
}

所有 vm 和 pm 详细信息都在客户端填充(我已经通过打印它们进行了检查)。在服务器端,只有 pm 详细信息会在传输后填充到本地 pm 对象上。在服务器端显示 vm 详细信息会给我空值。

我的疑问:

  1. 不传输一个也有子对象的对象,是否意味着整个父子对象都被传输了?
  2. 我必须分别手动传输 vm 和 pm 对象吗?

编辑-

public class VM implements Serializable{
       String osType,vmName; //on server's side, these are still null
       int UUID,osVersion;   // on the server's side, these are 0. Are integer variables initialised to a default of zero?
       }

【问题讨论】:

  • 被序列化对象持有的对象也应该被序列化,只要它们实现了 Serializable 接口(如果没有,这应该抛出异常)并且它们没有被标记为瞬态。您确定 VM 变量在您序列化 PM 对象时实际上引用了一个有效的引用吗?
  • 嗯,您似乎确实检查了我的最后一个问题。
  • @HovercraftFullOfEels- 是的,我让 VM 类也实现了 Serializable 。 VM 是有效的,因为一旦我填充了 vm 对象,我就会显示内容。
  • 是的,在我的第一条评论被锁定后,我看到了。看起来您将不得不进一步调试它,也许通过简化代码以尝试隔离问题。
  • 你觉得我怎么能做到?我已经测试了填充并检查客户端是否正在发送它。另外,我检查了服务器确实收到了最新的副本。我还向 vm 添加了其他属性。我在服务器端单独显示了所有 vm 属性。虽然字符串值仍为 NUll,但 int 值为 0(在客户端为 8)。怎么样?

标签: java sockets serialization


【解决方案1】:

正如@Hovercraft... 提到的,是的,如果字段不标记为transient,VM 也应该被序列化。 transient (definition) 是表示序列化应该忽略该字段的 Java 关键字。如果vm 字段被标记为transient,那么它将始终作为null 到达接收器。在您的情况下,vm 字段应该不使用瞬态关键字。

不确定问题出在哪里,但我认为我会评论一些事情。

  • 是否可能在不同的 PC 上安装不同版本的软件?接收方确定在PM 中有vm 字段吗?
  • 每次更改类以确保PM 类在客户端和服务器上匹配时,增加您的Serializable id 可能会很好。您还可以删除 serialVersionUID 并让 VM 生成一个确保您在两侧都有匹配的类。如果它们不匹配,这将给出一个例外。
  • 您可能知道这一点,但请注意PM 的大小不要超过数据包大小,因为您使用的是DatagramPacket。它们可以被分割、重新排序、根本不交付等等。我怀疑ObjectInputStream 会给你一个部分对象。
  • 确保捕获异常并正确记录相关消息,尽管如果抛出异常,您将不会获得 任何 对象。

为了后代,建议生成serialVersionUID,而不是依赖VM的运行时计算。更改对象时是否更改 ID 取决于您要实现的分布式系统类型。

顺便说一句,serialVersionUID 是每个类的标识符,可帮助 Java VM 验证被序列化的类是否与被反序列化的类的定义相匹配。当对象被反序列化时,VM 会查找对象的类并确保 UID 相同。如果不是,则在反序列化对象时抛出InvalidClassException 异常。两个不同的类是否具有相同的 UID 并不重要。

希望这会有所帮助。

【讨论】:

  • @Gray- 正如你所说,1. VM 已序列化。瞬态是指必须将 pm 和 vm 对象声明为瞬态,对吗?(也尝试过,但没有变化)。 2. 两台电脑的所有软件版本相同。由于我无法收到任何 vm 详细信息,我如何确保完全收到 vm 详细信息? 3.我应该为 VM 和 PM 类生成 2 个 UID 并查看它们是否匹配?谢谢!
  • 我已编辑我的答案以提供有关瞬态的更多详细信息。接收器上的vm 字段是否为空?我不明白“虚拟机详细信息”的问题。我在答案中添加了有关 UID 的更多详细信息。 PM 和 VM 的 UID 完全不相关。它们可以匹配或不匹配,但这并不重要。
  • @Gray- vm 字段在接收器上为空。我在客户端和服务器端都声明了 private static final long serialVersionUID=1L。关于更新虚拟机详细信息,我不知道这是否有助于讨论,但我正在编辑我的问题。
  • 这是尝试@P Ramesh 的一件事。在客户端和服务器上记录数据包的大小(byteStream.size() 与recvBuf.size())。确保它匹配。然后在 just 客户端上添加 transient 关键字,并确保大小下降并且在服务器上是相同的。
  • 您提到“我在客户端和服务器端都声明了 private static final long serialVersionUID=1L”。可以肯定的是,您应该使用 same PM 和 VM 类文件编译您的客户端和服务器程序。您不应该在您尝试匹配的客户端和服务器项目中有单独的类。
【解决方案2】:

数组是 Java 中的一等对象,它们不是可序列化的对象。这个问题有三个基本的解决方案:

字段可以声明为瞬态的。 可以实现 writeObject( )/readObject( ) 方法。 可以声明serialPersistentFields。

希望这会有所帮助。

【讨论】:

    猜你喜欢
    • 2011-06-21
    • 1970-01-01
    • 2011-11-26
    • 2023-03-23
    • 2011-05-14
    • 1970-01-01
    • 2012-06-07
    • 2017-04-24
    • 1970-01-01
    相关资源
    最近更新 更多