我一直在尝试调试这个2个小时,我无法解释它。 我有一个服务器和一个客户端。 (服务器管理一些拍卖)。
会发生什么:
客户端请求一些东西,服务器发回数据,客户端收到它就好了。
客户端向服务器发送内容,服务器更新一些数据。
客户端发出与第一次相同的请求(1.),服务器发回更新的请求 数据,但客户端不接收新的更新数据,而是接收旧数据(因为它在第一个请求中得到它(1.)。
正在发送的数据只是一个带有两个List-s的Java Bean。 代码:
// CLIENT CLASS
// creates socket, sends and listens on the socket
// listening is done on a separate thread
public class ServerConnector {
private Socket socket = null;
private ObjectOutputStream out = null;
private Display display;
private ServerListener listener;
public ServerConnector(Display display) {
this.display = display;
try {
socket = new Socket("localhost",33333);
out = new ObjectOutputStream(socket.getOutputStream());
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
listener = new ServerListener(socket, display);
new Thread(listener).start();
}
public void sendRequest(Request request) {
try {
out.writeObject(request);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
class ServerListener implements Runnable {
private Socket socket;
private ObjectInputStream in = null;
private Display display;
public ServerListener(Socket socket,Display display) {
this.socket = socket;
this.display = display;
try {
in = new ObjectInputStream(socket.getInputStream());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
public void run() {
Response response =null;
try {
while ((response = (Response)in.readObject()) != null) {
if (response.getCars().size() > 0) {
display.showAvailableCars(response.getCars());
}
if(response.getAucs().size() > 0) {
List<Auction> auctionz = response.getAucs();//HERE 1st time it gets the GOOD data, 2nd time should get UPDATED DATA but instead receives the OLD DATA (same as 1st time).
display.showOpenAuctions(auctionz);
}
response = null;
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
//CLIENT CLASS
// controls when something should be sent, and print out responses
public class Display {
Scanner console = new Scanner(System.in);
ServerConnector server = new ServerConnector(this);
List<Car> cars;
List<Auction> aucs;
public void show() {
int opt = 0;
System.out.println("1. Show available cars for auction.");
System.out.println("2. Show open auctions.");
opt = console.nextInt();
Request request = new Request();
if (opt == 1)
request.setRequest(Request.GET_CARS);
if (opt == 2) {
request.setRequest(Request.GET_OPEN_AUCTIONS);
}
server.sendRequest(request);
}
public void showAvailableCars(List<Car> cars) {
int i = 0;
for (Car c : cars ){
i++;
System.out.println(i +". " + c.getMaker() + " " + c.getModel() + " price: " + c.getPrice());
}
System.out.println("Select car to open Auction for:");
int selectedCar = console.nextInt();
if (selectedCar != 0) {
if (selectedCar <= cars.size()) {
Request request= new Request();
request.setRequest(Request.OPEN_AUCTION);
Car c = cars.get(selectedCar-1);
request.setCar(c);
server.sendRequest(request);
}
}
show();
}
public void setCars(List<Car> cars) {
this.cars = cars;
}
public void showOpenAuctions(List<Auction> aucs2) {
int i = 0;
for (Auction auc : aucs2) {
i++;
System.out.println(i+ ". " + auc.getCar().getModel() + " " + auc.getCar().getMaker() + " last price: " + auc.getPrice());
}
System.out.println("You can now make offers");
System.out.println("Input auction number:");
int selectedAuction = 0;
selectedAuction = console.nextInt();
if (selectedAuction > 0 && selectedAuction <= aucs2.size()) {
System.out.println("Offer new price:");
int price = console.nextInt();
Request request= new Request();
request.setRequest(Request.MAKE_OFFER);
request.setAuctionId(aucs2.get(selectedAuction-1).getId());
request.setPrice(price);
server.sendRequest(request);
}
show();
}
public void setOpenAuctions(List<Auction> aucs2) {
this.aucs = aucs2;
}
}
// SERVER CLASS : send and receives
public class ClientManager implements Runnable {
private AuctionManager manager = new AuctionManagerImpl();
private Socket client;
private ObjectInputStream in = null;
private ObjectOutputStream out = null;
public ClientManager(Socket socket) {
this.client = socket;
try {
in = new ObjectInputStream(client.getInputStream());
out = new ObjectOutputStream(client.getOutputStream());
} catch(Exception e1) {
try {
e1.printStackTrace();
client.close();
}catch(Exception e) {
System.out.println(e.getMessage());
}
return;
}
}
@Override
public void run() {
Request req = null;
try {
while ((req = (Request)in.readObject()) != null) {
if (req.getRequest() != null) {
if (req.getRequest().equals(Request.GET_CARS)) {
Response response = new Response();
response.setCars(manager.getAvailableCars());
out.writeObject(response);
continue;
}
if (req.getRequest().equals(Request.OPEN_AUCTION)) {
manager.openAuction(req.getCar());
continue;
}
if (req.getRequest().equals(Request.GET_OPEN_AUCTIONS)) {
Response response = new Response();
response.setAucs(manager.getHoldedAuctions()); //this line ALWAYS sends to the client GOOD, UPDATED DATA
out.writeObject(response);
out.flush();
continue;
}
if (req.getRequest().equals(Request.MAKE_OFFER)) {
Auction auction = manager.getOpenAuction(req.getAuctionId());
manager.updateAuction(auction, req.getPrice(),client.getRemoteSocketAddress().toString());
continue;
}
}
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
答案 0 :(得分:3)
可能是因为您使用的是ObjectOutputStreams
。请记住,ObjectOutputStreams
将缓存写入它们的所有对象,以便将来再次写入相同的对象时,它可以编写反向引用而不是重写整个对象。在编写对象图时,这是必要的。
您的代码片段:
if (req.getRequest().equals(Request.GET_CARS)) {
Response response = new Response();
response.setCars(manager.getAvailableCars());
out.writeObject(response);
continue;
}
正在编写manager.getAvailableCars()
返回的对象。下一次收到请求时,会写入相同的对象(但现在具有不同的内容) - 但ObjectOutputStream
不知道新内容,因此它只写了一个反向引用。另一端的ObjectInputStream
看到后引用并返回它上次读取的同一对象,即原始数据。
您可以在每次回复后致电ObjectOutputStream.reset()
来解决此问题。这将清除流的缓存。
答案 1 :(得分:2)
请参阅ObjectOutputStream.writeUnshared()和.reset()。
答案 2 :(得分:0)
确定。我刚刚找到了解决方案。 从这里http://java.sun.com/developer/technicalArticles/ALT/sockets/:
对象序列化陷阱
使用对象序列化时,请务必记住ObjectOutputStream维护一个哈希表,将写入流的对象映射到句柄。当第一次将对象写入流时,其内容将被复制到流中。但是,后续写入会导致正在写入流的对象的句柄。这可能会导致一些问题:
如果将对象写入流然后再次修改和写入,则在对流进行反序列化时将不会注意到这些修改。同样,原因是后续写入会导致写入句柄,但修改后的对象不会复制到流中。要解决此问题,请调用ObjectOutputStream.reset方法,该方法丢弃已发送对象的内存,以便后续写入将对象复制到流中。 将大量对象写入ObjectOutputStream后,可能会抛出OutOfMemoryError。这样做的原因是哈希表维护对应用程序可能无法访问的对象的引用。只需调用ObjectOutputStream.reset方法将对象/句柄表重置为初始状态即可解决此问题。在此调用之后,所有先前编写的对象都有资格进行垃圾回收。 reset方法将流状态重置为刚刚构造的流状态。在序列化对象时,可能不会调用此方法。对此方法的不适当调用会导致IOException。