2011-05-26 57 views
1

我得到了一個簡單的基於套接字的客戶端 - 服務器應用程序,每個連接都獲得自己的線程。 目前的方案是這樣的:從ObjectInputStream中讀取僅在連接時返回對象

  • 板 - 是客戶,系列化,普通Java對象
  • ActiveSessions之間共享的對象 - 所有的連接被添加到列表
  • 廣播公司 - 當董事會已改變,董事會發送給其他客戶

問題是,每個線程連接和recevies董事會對象,但是當它再次發送再次發送相同的對象,但在服務器端對象行爲正確。

public static void main(String[] args) { 
    Board gameBoard = new Board(); 
    ActiveSessions sessions = new ActiveSessions(); 
    Broadcaster broadcaster = new Broadcaster(sessions, gameBoard); 

    try { 
     ServerSocket socket = new ServerSocket(1234); 
     // Timeout after what no more new connections are not accepted. 
     socket.setSoTimeout(30 * 1000); 
     logger.info("Server started on port " + socket.getLocalPort()); 

     while (true) { 
      SessionHandler session = new SessionHandler(socket.accept(), gameBoard, broadcaster); 
      sessions.addSession(session); 
      session.start(); 
     } 

    } catch (SocketTimeoutException e1) { 
     logger.info("No more new connecions are accpeted, start game or end"); 
     gameBoard.setGameState(GameState.PLAYING); 
     logger.info("Set the gamestate to " + gameBoard.getGameState()); 
    } catch (IOException e) { 
     logger.info("I/O error " + e.getMessage()); 
    } 

SessionHandler,每個連接有自己的線程 包服務器;

import game.Board; 
    import game.Turn; 

    import java.io.EOFException; 
    import java.io.IOException; 
    import java.io.ObjectInputStream; 
    import java.io.ObjectOutputStream; 
    import java.net.Socket; 
    import java.net.SocketException; 
    import java.util.logging.Logger; 

    public class SessionHandler extends Thread { 

     private Board gameBoard; 
     private Socket socket; 
     private Broadcaster broadcaster; 
     private boolean firstConnect = true; 

     private ObjectOutputStream out; 
     private ObjectInputStream in; 
     private static final Logger logger = Logger.getLogger(SocketServer.class.getName()); 

     public SessionHandler(Socket socket) { 
      this.socket = socket; 
     } 

     public SessionHandler(Socket accept, Board gameBoard, Broadcaster broadcaster) { 
      this(accept); 
      this.gameBoard = gameBoard; 
      this.broadcaster = broadcaster; 
     } 

     @Override 
     public void run() { 
      try { 
       out = new ObjectOutputStream(socket.getOutputStream()); 
       in = new ObjectInputStream(socket.getInputStream()); 

       while (true) { 
        Turn turn = (Turn) in.readObject(); 
        if (turn != null) { 
         if (firstConnect) { 
          gameBoard.addPlayer(turn.getWhoseTurn()); 
          firstConnect = false; 
         } 

         // Add the turn to game board and make validation 
         gameBoard.increaseTurns(); 
         broadcaster.send(); 
        } 
        System.out.println("Turns made " + gameBoard.getTurns() + " players " + gameBoard.getPlayers() + " dice score " + turn.getDiceScore()); 
       } 
      } catch (EOFException e1) { 
       logger.warning("Problem reading the object output"); 
      } catch (SocketException e) { 
       if ("Connection reset".equals(e.getMessage())) { 
        System.out.println("Client disconnected, performing cleanup"); 
       } else { 
        logger.warning("Connection between client lost " + Thread.currentThread()); 
       } 
      } catch (Exception e) { 
       e.printStackTrace(); 
       System.exit(-1); 
      } 
     } 

     public void sendTheGameBoard() { 
      try { 
       out.writeObject(this.gameBoard); 
       out.flush(); 
      } catch (IOException e) { 
       logger.warning("Problem with sending game board object " + e.getMessage()); 
      } 
     } 
    } 
    class Broadcaster { 

    private ActiveSessions activeSessions; 

    public Broadcaster(ActiveSessions aa, Board board) { 
     this.activeSessions = aa; 
    } 

    public void send() { 
     // Broadcast board forever 
      synchronized (activeSessions) { 
       Iterator<SessionHandler> active = activeSessions.iterator(); 

       while (active.hasNext()) { 
        SessionHandler session = active.next(); 

        if (!session.isAlive()) { 
         active.remove(); 
         session.interrupt(); 
        } else { 
         session.sendTheGameBoard(); 
        } 
       } 
      } 

    } 
} 

回答

4

閱讀http://java.sun.com/javase/technologies/core/basic/serializationFAQ.jsp#handle。 ObjectOutputStream有一個緩存,以避免多次發送同一個對象。您必須重置流以再次發送副本。

+0

謝謝你,我尋找修復很多小時,並沒有找到這個。我通過添加reset()更正了發送代碼。 – 2011-05-26 17:03:49

+0

你也可以使用'writeUnshared()'每次發送對象的新副本。但是'reset()'對於避免內存泄漏很有用。如果你不叫它,它會記住你發送的每一個對象。 ;) – 2011-05-26 17:13:54

+1

@Peter Lawrey @jurka澄清:寫僅共享'unshares'提供給writeObject()的實際對象。它引用的任何對象都保持共享。重置()'取消'所有寫入到目前爲止。 – EJP 2011-05-27 00:59:50

相關問題