Java Sockets - Receive But Not Forwarded!

I tried to debug this for 2 hours, and I just can't explain it. I have a server and client. (the server manages some auctions).

What's happening:

  • The client requests something, the server sends the data back, and the client receives it just fine.

  • The client sends something to the server, and the server updates some data.

  • The client makes the same request as the first time (1.), the server sends the updated data, but the client does not receive new update data, instead it receives the old data (since it received it in the first request (1.).

The transmitted data is just a Java Bean with two List-s. And the code:

// 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(); } } } 
+4
source share
3 answers

This may be because you are using ObjectOutputStreams . Remember that ObjectOutputStreams will cache all objects written for them, so if the same object is written again in the future, it can write a backlink instead of re-writing the entire object. This is necessary when writing a graph of objects.

Your code snippet:

 if (req.getRequest().equals(Request.GET_CARS)) { Response response = new Response(); response.setCars(manager.getAvailableCars()); out.writeObject(response); continue; } 

records the object returned by manager.getAvailableCars() . The next time the request is received, the same object will be recorded (but now with different content), but ObjectOutputStream does not know about the new content, so it just writes a backlink. ObjectInputStream at the other end sees a backlink and returns the same object that it read the last time, that is, the original data.

You can fix this by calling ObjectOutputStream.reset() after each response. This will clear the stream cache.

+3
source

See ObjectOutputStream.writeUnshared () and .reset ().

+2
source

Ok I just found a solution. from here http://java.sun.com/developer/technicalArticles/ALT/sockets/ :

Serializing Pitfall Objects

When working with serializing objects, it is important to keep in mind that ObjectOutputStream maintains a hash table that maps objects written to the stream to a descriptor. When an object is written to the stream for the first time, its contents will be copied to the stream. However, subsequent entries result in a handle to the object that is being written to the stream. This can lead to several problems:

If an object is written to a stream, and then modified and written a second time, the changes will not be noticed when the stream is deserialized. Again, the reason is that a subsequent write causes the descriptor to be written, but the modified object is not copied to the stream. To solve this problem, call the ObjectOutputStream.reset method, which discards the sending memory of the object so that a subsequent write will copy the object to the stream. An OutOfMemoryError can be thrown after writing a large number of objects to an ObjectOutputStream. The reason for this is that the hash table supports references to objects that might otherwise be inaccessible to the application. This problem can be solved by simply calling the ObjectOutputStream.reset method to reset the object / handle table to its original state. After this call, all previously written objects will have the right to garbage collection. The reset method resets the state of a stream as if it had just been constructed. This method cannot be called when objects are serialized. Inappropriate calls to this method throw an IOException.

0
source

All Articles