上下文:客户端正在向服务器发送SOSPFPacket对象(通过TCP),该对象具有各种属性,例如Vector<LSA> lsaArray
。 LSA
本身具有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.java
的Runnable
线程来完成。以下是相关方法: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()
,但请注意,它仍然共享引用的对象,即,如果您尝试发送包含已添加和已更改元素对象的列表,它将发送新列表和新元素对象,但不会发送已更改的元素对象。