2014-02-09 54 views
0

我有一個簡單的XO遊戲,其中有多個客戶端互相玩XO國際象棋。我建立了一個與每個連接的客戶端進行通信的服務器。 (當客戶端連接到服務器時,我創建一個新類(實現Runnable)處理該套接字,然後啓動線程...我有一個List存儲外部類中的所有客戶端)當客戶端想要啓動一個遊戲時,他使自己成爲一個新的服務器,並將該房間的信息(他的名字,IP地址,端口...)發送到服務器。而在服務器中,我有一個列表,將該房間添加到列表中,並將其發送給其他客戶端。Java套接字 - 更新實例變量線程列表

public class ServerController { 
private ServerView view; 
private ServerSocket myServer; 
private int serverPort = 8881; 
private List<ExchangeData> list; 
private volatile List<Room> rooms; 


public ServerController(ServerView view) { 
    this.view = view; 
    openServer(serverPort); 
    view.showMessage("TCP server is running..."); 
    list = new ArrayList<ExchangeData>(); 
    rooms = new ArrayList<Room>(); 
    while (true) { 
     listening(); 
    } 

} 

private void openServer(int portNumber) { 
    .... 
} 

private void listening() { 

    try { 
     Socket clientSocket = myServer.accept(); 

     ExchangeData ex = new ExchangeData(clientSocket); 
     Thread thread = new Thread(ex); 
     thread.start(); 
     list.add(ex); 

    } catch (IOException e) { 
     System.out.print(e.toString()); 
    } 
} 

class ExchangeData implements Runnable { 
    private Socket clientSocket; 
    private ObjectOutputStream oos; 
    private ObjectInputStream ois; 
    private boolean run = true; 

    public ExchangeData(Socket socket) { 
     try { 
      clientSocket = socket; 
      oos = new ObjectOutputStream(clientSocket.getOutputStream()); 
      ois = new ObjectInputStream(clientSocket.getInputStream()); 
     } catch (Exception ex) { 
      System.out.println(ex.toString()); 
     } 


    } 

    public void closeConnection() { 
     ..... 
    } 



    public void sendData(Object o) { 
     try { 

      oos.writeObject(o); 
     } catch (Exception ex) { 
      view.showMessage(ex.toString()); 
     } 
    } 

    public void run() { 
     try { 

      while (run) { 
       Object data = ois.readObject(); 
       if (data instanceof Object[]) { 
        Object[] o = (Object[]) data; 
        String s = o[0].toString(); 

        if (s.equalsIgnoreCase("Open Room")) { 

         Room r = (Room) o[1]; 
         String username = r.getUsername(); 
         InetSocketAddress isa = r.getIsa(); 
         **rooms.add(r);** 
         List<Room> l = new ArrayList<Room>(rooms); 

         for (ExchangeData ex : list) { 
          ex.sendData(username + " Open A room at : " 
            + isa); 
          **ex.sendData(rooms);** 

         } 

        } else if (s.equalsIgnoreCase("Close Room")) { 
         Room room = (Room) o[1]; 
         rooms.remove(room); 
         List<Room> l = new ArrayList<Room>(rooms); 
         for (ExchangeData ex : list) { 
          ex.sendData(l); 
         } 
        } 

       } else if (data instanceof String) { 
        String s = data.toString(); 
        if (s.equalsIgnoreCase("Quit")) { 
         stopThread(); 
         list.remove(this); 
         closeConnection(); 

        } 
       } 

      } 

     } catch (Exception ex) { 
      try { 
       stopThread(); 
       list.remove(this); 
      } catch (ExceptionInInitializerError e) { 
       e.printStackTrace(); 

      } 
     } 
    } 
} 

}

問題是,當我執行rooms.add(R),其他線程似乎沒有看到, 「更新」。例如: 1)第一位玩家打開房間 - 房間大小爲1; 2)第二個玩家打開一個新房間 - 房間大小現在是2(我試圖在sendData(Object o)方法中添加一些代碼行,它在房間的大小上打印並且全部是2.但是當行ObjectoutputStream .write(o)被執行,它實際上寫入線程(第一個玩家)在之前保存的房間的值爲1. 如果我創建像List一樣的新列表l = new ArrayList(房間),就像我對「Closing Room 「然後發送這個,它工作,我不明白爲什麼?有人請向我解釋這個。對不起,我的英語不好:(

回答

1

當你通過給定的ObjectOutputStream幾次發送相同的對象時,流寫入第一次是完整的對象狀態,但是之後只發送一些對該對象的引用。

這允許發送具有循環引用的對象的複雜圖形,而不消耗太多帶寬並且不進入無限循環(發送引用B的B,因此發送引用A的B,發送引用B的B等等)。

因此,如果您想發送清單副本,您必須首先使用ObjectOutputStream的reset()方法。

+0

哇,非常感謝Nizet,我花了好幾天的時間來閱讀關於線程安全,併發等方面的知識,希望我可以爲你投票。 – user2747502