上下文:客户端正在向服务器发送SOSPFPacket对象(通过TCP),该对象具有各种属性,例如Vector<LSA> lsaArrayLSA本身具有LinkedList<LinkDescription> links属性。在我的测试案例中,有两条消息正在发送。在这两个消息中, vector 中只有一个LSA。在第一个消息中,LSA有一个LinkDescription,在第二个消息中,它有两个。发送消息时,我增加messageId

服务器收到两 strip 有正确ID的消息,但是在第二条消息中,链接仅包含一个链接,而不是两个。我无能为力...

以下是对象的实现:

import java.io.*;
import java.util.Vector;

public class SOSPFPacket implements Serializable {
  public final static short HELLO = 0;
  public final static short LSU = 1;
  public final static short OVER_BURDENED = 2;
  public static int id = Integer.MIN_VALUE;

  public String srcProcessIP;
  public short srcProcessPort;
  public String srcIP;
  public String dstIP;
  public short sospfType; //0 - HELLO, 1 - LinkState Update, 2 - Over Burdened
  public String routerID;
  public int messageId = id++;
  public String neighborID; //neighbor's simulated IP address
  public Vector<LSA> lsaArray = new Vector<>();
  public String lsaInitiator = null;
}
import java.io.Serializable;
import java.util.LinkedList;

public class LSA implements Serializable {
  public String linkStateID;
  public int lsaSeqNumber = Integer.MIN_VALUE;
  public LinkedList<LinkDescription> links = new LinkedList<LinkDescription>();

  @Override
  public String toString() {
    StringBuffer sb = new StringBuffer();
    sb.append(linkStateID + ":").append(lsaSeqNumber + "\n");
    for (LinkDescription ld : links) {
      sb.append(ld);
    }
    sb.append("\n");
    return sb.toString();
  }
}
import java.io.Serializable;

public class LinkDescription implements Serializable {
  public String linkID;
  public int portNum;
  public int tosMetrics;

  public LinkDescription() {}

  public LinkDescription(String linkID, int portNum, int tosMetrics) {
    this.linkID = linkID;
    this.portNum = portNum;
    this.tosMetrics = tosMetrics;
  }

  public String toString() {
    return linkID + ","  + portNum + "," + tosMetrics;
  }
}

要发送消息,我通过实现Client.javaRunnable线程来完成。以下是相关方法:
public void run() {
    try {
        _outputStream = new ObjectOutputStream(_clientSocket.getOutputStream());
        sendMessage(SOSPFPacket.HELLO);
        _inputStream = new ObjectInputStream(_clientSocket.getInputStream());
        SOSPFPacket message = Util.receiveMessage(_inputStream);

        if (message.sospfType == SOSPFPacket.OVER_BURDENED) {
            System.out.println("Removing link with router " + message.srcIP + "...");
            _router.removeLink(_remoteRouterIP);
            return;
        }

        _remoteRouterDescription.setStatus(RouterStatus.TWO_WAY);
        _router.addLinkDescriptionToDatabase(_remoteRouterDescription, _link.getWeight());
        sendMessage(SOSPFPacket.HELLO);
        message = Util.receiveMessage(_inputStream);

        if (message.sospfType == SOSPFPacket.LSU) {
            _router.synchronize(message.lsaArray);
        }

        _router.propagateSynchronization(message.lsaInitiator, message.srcIP);
    } catch (IOException e) {
        e.printStackTrace();
    }
}

private void sendMessage(short messageType) {
    try {
        SOSPFPacket message = Util.makeMessage(_rd, _remoteRouterDescription, messageType, _router);
        _outputStream.writeObject(message);
        _outputStream.flush();
    } catch (IOException e) {
        e.printStackTrace();
    }
}
public class Util {
    public static SOSPFPacket makeMessage(RouterDescription local, RouterDescription external, short messageType, Router rd) {
        SOSPFPacket message = new SOSPFPacket();
        message.srcProcessIP = local.getProcessIPAddress();
        message.srcProcessPort = local.getProcessPortNumber();
        message.srcIP = local.getSimulatedIPAddress();
        message.dstIP = external.getSimulatedIPAddress();
        message.sospfType = messageType;
        message.routerID = local.getSimulatedIPAddress();
        message.neighborID = external.getSimulatedIPAddress();
        rd.getLsd().getStore().forEach((k, v) -> message.lsaArray.addElement(v));
        message.lsaInitiator = messageType == SOSPFPacket.LSU ? message.srcIP : null;

        return message;
    }

    public static SOSPFPacket receiveMessage(ObjectInputStream inputStream) {
        SOSPFPacket receivedMessage = null;

        try {
            receivedMessage = (SOSPFPacket) inputStream.readObject();

            String messageType;

            switch (receivedMessage.sospfType) {
                case SOSPFPacket.HELLO:
                    messageType = "HELLO";
                    break;
                case SOSPFPacket.LSU:
                    messageType = "LINKSTATEUPDATE";
                    break;
                case SOSPFPacket.OVER_BURDENED:
                    messageType = "OVER_BURDENED";
                    break;
                default:
                    messageType = "UNKNOWN_STATE";
                    break;
            }

            System.out.println("received " + messageType + " from " + receivedMessage.srcIP + ";");
        } catch (ClassNotFoundException e) {
            System.out.println("No message received.");
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return receivedMessage;
    }
}

服务器在收到负责接收消息的新连接时,实例化一个私有(private)ClientServiceThread
private class ClientServiceThread implements Runnable {
    Socket _clientSocket;
    Thread _runner;

    ClientServiceThread(Socket s) {
        _clientSocket = s;
        _runner = new Thread(this);
    }

    public Thread getRunner() { return _runner; }

    public void run() {
        ObjectInputStream inputStream = null;
        ObjectOutputStream outputStream = null;

        try {
            inputStream = new ObjectInputStream(_clientSocket.getInputStream());
            outputStream = new ObjectOutputStream(_clientSocket.getOutputStream());

            while (true) {
                try {
                    SOSPFPacket receivedMessage = Util.receiveMessage(inputStream);

                    //some logic not relevant since the receivedMessage is already not correct
                }
            }
        }
    }
}

同样,除SOSPFPacket外,所有Vector<LSA> lsaArray字段均已正确接收。

编辑:我也尝试过在sendMessage(SOSPFPacket.HELLO)之后发送第三个_router.propagateSynchronization(message.lsaInitiator, message.srcIP);。这次,正在发送的消息包含两个LSA,第一个包含两个LinkDescription,第二个包含一个LSA。服务器接收到两个LinkDescription,但是在第一个LSA中仅接收到第一个LinkDescription。在所有三个消息中,消息标识都是正确的。
如果我第二次运行所有内容(即为已经运行的路由器创建一个新的Client和一个新的ClientService线程),则服务器才最终在第一个LSA中收到两个ojit_code。

最佳答案

Java发送对已经序列化的对象的引用,以保持对象图的完整性。

您应该在每个ObjectOutputStream.reset()之后调用writeObject()

或使用ObjectOutputStream.writeUnshared(),但请注意,它仍然共享引用的对象,即,如果您尝试发送包含已添加和已更改元素对象的列表,它将发送新列表和新元素对象,但不会发送已更改的元素对象。

10-08 14:57