ObjectInputStream 接收到的对象不等于 ObjectOutputStream 发送的对象
Object recieved by ObjectInputStream not equal to Object sent by ObjectOutputStream
我正在做一个项目,其中执行 Java 中的对象 IO。问题出在客户端和服务器之间的关系上,位于服务器的ObjectOutputStream发送的对象不等于客户端接收到的class。
这里写的 ClientLoop class 在 Client 上不存在。(我可能应该更改它以避免混淆。)为每个 Client 创建一个 ClientLoop,来自 Socket,即检测到并建立来自服务器的连接。它侦听客户端 发送的下一个对象 并将其发送到确定操作的对象管道。
A question possibly anticipating the underlying technology from the object streams
public class ClientLoop extends Loop implements Sendable {
private final Commander commander;
private final Socket socket;
private final ObjectInputStream input;
private final ObjectOutputStream output;
private final Server server;
public ClientLoop(Server server, Socket socket) throws IOException {
this.server = server;
this.socket = socket;
//we need to construct the output stream first
//to discard the stream header
this.output = new ObjectOutputStream(socket.getOutputStream());
this.input = new ObjectInputStream(socket.getInputStream());
this.commander = new Commander(server, socket, this);
}
@Override
public void loop() {
try {
try {
Object next = input.readObject();
String className = next.getClass().getName();
System.out.println("Server Receive: " + next);
if (className.equals("com.meti.server.util.Command")) {
commander.runCommand(castIfOfInstance(next, Command.class));
//this section of code here says that whenever an AssetChange is being sent by the client.
} else if (next instanceof AssetChange) {
AssetChange assetChange = castIfOfInstance(next, AssetChange.class);
assetChange.update(server.getAssetManager().getAsset(assetChange.getAssetPath()));
//has been change, update all clientloops
} else {
getInstance().log(Level.WARNING, "Found no type handling " +
"for class type " + className);
}
} catch (EOFException e) {
socket.close();
setRunning(false);
}
} catch (Exception e) {
getInstance().log(Level.WARNING, e);
}
}
@Override
public void send(Serializable serializable, boolean flush) throws IOException {
System.out.println("Server Send: " + serializable);
output.writeObject(serializable);
if (flush) {
output.flush();
}
}
}
这里的指挥官 class 是从 ClientLoop class 提供额外的功能,其中 class 太难理解了。它的存在只是为了容纳 运行 命令的方法。它可能不是最好的代码结构,稍后会修复它。
public class Commander {
private Server server;
private Socket socket;
private Sendable sendable;
public Commander() {
}
public Commander(Server server, Socket socket, Sendable sendable) {
this.server = server;
this.socket = socket;
this.sendable = sendable;
}
public void runCommand(Command next) throws IOException {
if ("login".equals(next.getName())) {
String password = (String) next.getArgs()[0];
if (password.equals(server.getPassword())) {
getInstance().log(Level.INFO, "Client " + socket.getInetAddress() + " has connected with valid password");
} else {
getInstance().log(Level.INFO, "Client has invalid password, kicking out!");
socket.close();
}
} else if ("disconnect".equals(next.getName())) {
socket.close();
} else if ("list".equals(next.getName())) {
Cargo<String> cargo = new Cargo<>();
HashMap<String, Asset<?>> assets = server.getAssetManager().getAssets();
ArrayList<String> paths = new ArrayList<>();
paths.addAll(assets.keySet());
cargo.getContents().addAll(paths);
sendable.send(cargo, true);
} else if ("get".equals(next.getName())) {//should be declared other side...
String path = Utility.castIfOfInstance(next.getArgs()[0], String.class);
Asset<?> asset = server.getAssetManager().getAsset(path);
sendable.send(asset, true);
}
}
}
这是最近 运行 会话的日志。每当它说 "Server Receive" 时,就意味着服务器找到了客户端发送的对象,而当它说 "Server Send" 时,它就是服务器通过 ObjectInputStream 和 ObjectOutputStream 发送的对象。否则,它是一个通用的控制台语句。在日志的最后,显示服务器发送了一个名为
"Server Send:Asset{file=Nexus\sample.txt, content=Hello Server!asdxc}"。 (请注意,短语 "asdxc" 是在测试期间使用按钮混合创建的。它不包含任何代码意义。)
但是,问题就出在这里,在前面的语句中
"Receive: Asset{file=Nexus\sample.txt, content=Hello Server!}".
我从这个日志输出中得出结论,客户端中的ObjectInputStream和服务器中的ObjectOutputStream确实运行正常,但是服务器上的ObjectOutputStream发送的对象不等于对象立即由客户端上的 ObjectInputStream 接收。根据日志,服务器发送的对象只有一个,客户端没有正确注册。
Oct 31, 2017 8:11:11 AM com.meti.Main log
INFO: Starting application
Oct 31, 2017 8:11:50 AM com.meti.Main log
INFO: Reading directory
Oct 31, 2017 8:11:51 AM com.meti.Main log
INFO: Listening for clients
Oct 31, 2017 8:11:53 AM com.meti.Main log
INFO: Located client at /127.0.0.1
Server Receive: Command{name='login', args=[5875580034436271440]}
Oct 31, 2017 8:11:53 AM com.meti.Main log
INFO: Client /127.0.0.1 has connected with valid password
Server Receive: Command{name='list', args=[files]}
Server Send:com.meti.server.util.Cargo@68917144
Server Receive: Command{name='get', args=[Nexus\sample.txt]}
Server Send:Asset{file=Nexus\sample.txt, content=Hello Server!}
Receive: Asset{file=Nexus\sample.txt, content=Hello Server!}
Server Receive: com.meti.server.asset.text.TextChange@546c91cb
Server Receive: com.meti.server.asset.text.TextChange@1a2f571d
Server Receive: com.meti.server.asset.text.TextChange@7a308ae0
Server Receive: com.meti.server.asset.text.TextChange@5897f82d
Server Receive: com.meti.server.asset.text.TextChange@68c5d83d
Server Receive: com.meti.server.asset.text.TextChange@832ed87
Server Receive: com.meti.server.asset.text.TextChange@76ab11eb
Server Receive: Command{name='get', args=[Nexus\sample.txt]}
Server Send:Asset{file=Nexus\sample.txt, content=Hello Server!asdxc}
Receive: Asset{file=Nexus\sample.txt, content=Hello Server!}
如果需要更多代码来诊断问题,我在 GitHub link 处有完整的代码库:
https://github.com/Mathhman/Nexus
如果您使用不同的内容发送同一个对象两次,您需要使用 writeUnshared()
或 reset()
来确保它确实被再次发送。
我正在做一个项目,其中执行 Java 中的对象 IO。问题出在客户端和服务器之间的关系上,位于服务器的ObjectOutputStream发送的对象不等于客户端接收到的class。
这里写的 ClientLoop class 在 Client 上不存在。(我可能应该更改它以避免混淆。)为每个 Client 创建一个 ClientLoop,来自 Socket,即检测到并建立来自服务器的连接。它侦听客户端 发送的下一个对象 并将其发送到确定操作的对象管道。
A question possibly anticipating the underlying technology from the object streams
public class ClientLoop extends Loop implements Sendable {
private final Commander commander;
private final Socket socket;
private final ObjectInputStream input;
private final ObjectOutputStream output;
private final Server server;
public ClientLoop(Server server, Socket socket) throws IOException {
this.server = server;
this.socket = socket;
//we need to construct the output stream first
//to discard the stream header
this.output = new ObjectOutputStream(socket.getOutputStream());
this.input = new ObjectInputStream(socket.getInputStream());
this.commander = new Commander(server, socket, this);
}
@Override
public void loop() {
try {
try {
Object next = input.readObject();
String className = next.getClass().getName();
System.out.println("Server Receive: " + next);
if (className.equals("com.meti.server.util.Command")) {
commander.runCommand(castIfOfInstance(next, Command.class));
//this section of code here says that whenever an AssetChange is being sent by the client.
} else if (next instanceof AssetChange) {
AssetChange assetChange = castIfOfInstance(next, AssetChange.class);
assetChange.update(server.getAssetManager().getAsset(assetChange.getAssetPath()));
//has been change, update all clientloops
} else {
getInstance().log(Level.WARNING, "Found no type handling " +
"for class type " + className);
}
} catch (EOFException e) {
socket.close();
setRunning(false);
}
} catch (Exception e) {
getInstance().log(Level.WARNING, e);
}
}
@Override
public void send(Serializable serializable, boolean flush) throws IOException {
System.out.println("Server Send: " + serializable);
output.writeObject(serializable);
if (flush) {
output.flush();
}
}
}
这里的指挥官 class 是从 ClientLoop class 提供额外的功能,其中 class 太难理解了。它的存在只是为了容纳 运行 命令的方法。它可能不是最好的代码结构,稍后会修复它。
public class Commander {
private Server server;
private Socket socket;
private Sendable sendable;
public Commander() {
}
public Commander(Server server, Socket socket, Sendable sendable) {
this.server = server;
this.socket = socket;
this.sendable = sendable;
}
public void runCommand(Command next) throws IOException {
if ("login".equals(next.getName())) {
String password = (String) next.getArgs()[0];
if (password.equals(server.getPassword())) {
getInstance().log(Level.INFO, "Client " + socket.getInetAddress() + " has connected with valid password");
} else {
getInstance().log(Level.INFO, "Client has invalid password, kicking out!");
socket.close();
}
} else if ("disconnect".equals(next.getName())) {
socket.close();
} else if ("list".equals(next.getName())) {
Cargo<String> cargo = new Cargo<>();
HashMap<String, Asset<?>> assets = server.getAssetManager().getAssets();
ArrayList<String> paths = new ArrayList<>();
paths.addAll(assets.keySet());
cargo.getContents().addAll(paths);
sendable.send(cargo, true);
} else if ("get".equals(next.getName())) {//should be declared other side...
String path = Utility.castIfOfInstance(next.getArgs()[0], String.class);
Asset<?> asset = server.getAssetManager().getAsset(path);
sendable.send(asset, true);
}
}
}
这是最近 运行 会话的日志。每当它说 "Server Receive" 时,就意味着服务器找到了客户端发送的对象,而当它说 "Server Send" 时,它就是服务器通过 ObjectInputStream 和 ObjectOutputStream 发送的对象。否则,它是一个通用的控制台语句。在日志的最后,显示服务器发送了一个名为 "Server Send:Asset{file=Nexus\sample.txt, content=Hello Server!asdxc}"。 (请注意,短语 "asdxc" 是在测试期间使用按钮混合创建的。它不包含任何代码意义。)
但是,问题就出在这里,在前面的语句中 "Receive: Asset{file=Nexus\sample.txt, content=Hello Server!}".
我从这个日志输出中得出结论,客户端中的ObjectInputStream和服务器中的ObjectOutputStream确实运行正常,但是服务器上的ObjectOutputStream发送的对象不等于对象立即由客户端上的 ObjectInputStream 接收。根据日志,服务器发送的对象只有一个,客户端没有正确注册。
Oct 31, 2017 8:11:11 AM com.meti.Main log
INFO: Starting application
Oct 31, 2017 8:11:50 AM com.meti.Main log
INFO: Reading directory
Oct 31, 2017 8:11:51 AM com.meti.Main log
INFO: Listening for clients
Oct 31, 2017 8:11:53 AM com.meti.Main log
INFO: Located client at /127.0.0.1
Server Receive: Command{name='login', args=[5875580034436271440]}
Oct 31, 2017 8:11:53 AM com.meti.Main log
INFO: Client /127.0.0.1 has connected with valid password
Server Receive: Command{name='list', args=[files]}
Server Send:com.meti.server.util.Cargo@68917144
Server Receive: Command{name='get', args=[Nexus\sample.txt]}
Server Send:Asset{file=Nexus\sample.txt, content=Hello Server!}
Receive: Asset{file=Nexus\sample.txt, content=Hello Server!}
Server Receive: com.meti.server.asset.text.TextChange@546c91cb
Server Receive: com.meti.server.asset.text.TextChange@1a2f571d
Server Receive: com.meti.server.asset.text.TextChange@7a308ae0
Server Receive: com.meti.server.asset.text.TextChange@5897f82d
Server Receive: com.meti.server.asset.text.TextChange@68c5d83d
Server Receive: com.meti.server.asset.text.TextChange@832ed87
Server Receive: com.meti.server.asset.text.TextChange@76ab11eb
Server Receive: Command{name='get', args=[Nexus\sample.txt]}
Server Send:Asset{file=Nexus\sample.txt, content=Hello Server!asdxc}
Receive: Asset{file=Nexus\sample.txt, content=Hello Server!}
如果需要更多代码来诊断问题,我在 GitHub link 处有完整的代码库: https://github.com/Mathhman/Nexus
如果您使用不同的内容发送同一个对象两次,您需要使用 writeUnshared()
或 reset()
来确保它确实被再次发送。