通过套接字传输对象 - 可序列化

时间:2011-10-16 11:54:56

标签: java sockets serialization

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

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发生在两台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对象?
  3. 谢谢!

    编辑 -

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

2 个答案:

答案 0 :(得分:2)

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

不确定问题是什么,但我认为我会评论一些事情。

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

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

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

希望这有帮助。

答案 1 :(得分:1)

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

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

希望这会有所帮助。