2015-10-24 114 views
0

我做了一個應用程序,一個非常簡單的聊天功能ServerClient。一切工作完美的聊天功能,但只要我關閉Client,我得到 Server。該應用程序仍然正常運行,但我知道我不應該得到這個錯誤。我無法弄清楚爲什麼會發生這種情況。多線程服務器/客戶端聊天應用程序中的ConcurrentModificationException

這裏是我的服務器:

import javax.swing.*; 
import java.awt.*; 
import java.io.*; 
import java.net.Socket; 
import java.net.ServerSocket; 
import java.net.UnknownHostException; 
import java.util.ArrayList; 
import java.util.Iterator; 

public class Server extends JFrame { 

    JTextArea textArea; 
    // Serversocket. 
    private static ServerSocket serverSocket = null; 
    // Klientsocket. 
    private static Socket clientSocket = null; 
    // Datasamlingen av Threads. 
    private ArrayList<ClientThread> threads = new ArrayList<>(); 

    public static void main(String args[]) { 

     // Får (eller sätter default) port. 
     int portNumber = 2000; 
     if (args.length > 1) { 
      portNumber = Integer.valueOf(args[0]); 
     } 

     new Server(portNumber); 
    } 

    public Server(int portNumber) { 
     // Tar hand om JFrame. 
     setLayout(new BorderLayout()); 
     setVisible(true); 
     setLocation(750, 0); 
     setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); 

     //Skapar och adderar JTextArea. 
     textArea = new JTextArea(15, 60); 
     JPanel centerPanel = new JPanel(); 
     centerPanel.add(new JScrollPane(textArea)); 
     add(centerPanel, BorderLayout.CENTER); 
     pack(); 

     // Öppnar en serversoket med portnummret. Skriver också titel. 
     try { 
      serverSocket = new ServerSocket(portNumber); 
      changeTitle(); 
     } catch (IOException e) { 
      System.out.println(e); 
     } 

     // Vad som händer när man får ny klient. 
     // Skapar en klientsocket till varje anknytning till servern och skapar en new klienttråd. 
     while (true) { 
      try { 
       clientSocket = serverSocket.accept(); 
       ClientThread temp = new ClientThread(clientSocket, threads, this); 
       temp.start(); 
       threads.add(temp); 
      } catch (IOException e) { 
       System.out.println(e); 
      } 
     } 
    } 

    public void writeInTextArea(String message) { 
     // Metoden som används från ClientThread för att skriva någonting i textArean. 
     textArea.append(message); 
    } 

    public void changeTitle() { 
     // Metoden som används från ClientThread för att byta titel. 
     String host = ""; 
     int portNumber = 0; 
     try { 
      host = serverSocket.getInetAddress().getLocalHost().getHostAddress(); 
      portNumber = serverSocket.getLocalPort(); 
     } catch (UnknownHostException e) { 
      // Ignorera. 
     } 
     setTitle("CHAT | HOST: " + host + " | PORT: " + portNumber + " | NUMBER OF CLIENTS: " + threads.size()); 
    } 
} 


class ClientThread extends Thread { 

    private BufferedReader in = null; 
    private PrintWriter out = null; 
    private Socket clientSocket = null; 
    private ArrayList<ClientThread> threads; 
    private Server frame; 
    private String name; 

    public ClientThread(Socket clientSocket, ArrayList<ClientThread> threads, Server frame) { 
     this.clientSocket = clientSocket; 
     this.threads = threads; 
     this.frame = frame; 
    } 

    public void run() { 
     try { 
      // Öppnar nya input och output strömmar till den här klienten. Frågar efter klientens namn och 
      // informerar alla att en ny klient har loggat in. 
      in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); 
      out = new PrintWriter(clientSocket.getOutputStream(), true); 
      out.write("<<< Enter your name >>>\r"); 
      out.flush(); 
      name = in.readLine().trim(); 
      out.write("<<< Hi " + name + ". Welcome to the chat room >>>\n<<< Enter /quit in a new line to exit >>>\r"); 
      out.flush(); 
      //Byter titel. 
      frame.changeTitle(); 
      synchronized (this) { 
       for (ClientThread c : threads) { 
        if (c != this) { 
         c.out.write("<<< User " + name + " has connected >>>\r"); 
         c.out.flush(); 
        } 
       } 
       frame.writeInTextArea("<<< User " + name + " has connected >>>\n"); 
      } 

      // Eviga loopen. Kollar om klienten skriver någonting och broadcast:ar den till alla. 
      while (true) { 
       String line = in.readLine(); 
       if (line.startsWith("/quit")) { 
        // Om klienten skriver /quit så kommer man ut ur eviga loopen. 
        break; 
       } 
       for (ClientThread c : threads) { 
        c.out.write("<" + name + "> " + line + "\r"); 
        c.out.flush(); 
       } 
       frame.writeInTextArea("<" + name + "> " + line + "\n"); 
      } 

      // synchronized för att slippa krockar. 
      synchronized (this) { 
       // Informerar alla att klienten har loggat ut. 
       for (ClientThread c : threads) { 
        if (c != null && c != this) { 
         c.out.write("<<< User " + name + " has disconnected >>>\r"); 
         c.out.flush(); 
        } 
       } 
       frame.writeInTextArea("<<< User " + name + " has disconnected >>>\n"); 
       out.write("<<< Goodbye " + name + " >>>"); 
       out.flush(); 

       // Tar bort tråden från ArrayList:an och stoppar den. Byter också titel. 
       Iterator i = threads.iterator(); 
       while (i.hasNext()) { 
        ClientThread c = (ClientThread) i.next(); 
        if (c == this) { 
         c.interrupt(); 
         threads.remove(c); 
         frame.changeTitle(); 
        } 
       } 

       // Stänger out, in och klientsocket:en. 
       out.close(); 
       in.close(); 
       clientSocket.close(); 
      } 
     } catch (IOException e) { 
      // Vad som händer när en klient stängs ner (utan att skriva /quit, kanske om klienten stänger ner fönstret) 
      // Synchronized för att slippa krockar. 
      synchronized (this) { 
       // Informerar alla att klienten har loggat ut. 
       for (ClientThread c : threads) { 
        if (c != null) { 
         c.out.write("<<< User " + name + " has disconnected >>>\r"); 
         c.out.flush(); 
        } 
       } 
       frame.writeInTextArea("<<< User " + name + " has disconnected >>>\n"); 

       // Hittar tråden och stoppar och tar bort den från samlingen. Byter också titel. 
       Iterator i = threads.iterator(); 
       while (i.hasNext()) { 
        ClientThread c = (ClientThread) i.next(); 
        if (c == this) { 
         c.interrupt(); 
         threads.remove(c); 
         frame.changeTitle(); 
        } 
       } 

       // Stänger out, in och klientsocket. 
       try { 
        out.close(); 
        in.close(); 
        clientSocket.close(); 
       } catch (IOException e1) { 
        e1.printStackTrace(); 
       } 
      } 
     } 
    } 
} 

,這裏是我的客戶:

import javax.swing.*; 
import java.awt.*; 
import java.awt.event.ActionEvent; 
import java.io.*; 
import java.net.Socket; 

public class Client extends JFrame { 

    private Socket socket; 
    private BufferedReader in; 
    private PrintWriter out; 

    private JTextField inputTextField = new JTextField(60); 
    private JTextArea textArea = new JTextArea(15, 60); 

    // Tråd som kallas för att läsa rader från servern. 
    private Thread backgroundThread = new Thread(new Runnable() { 
     @Override 
     public void run() { 
      String line; 
      try { 
       in = new BufferedReader(new InputStreamReader(socket.getInputStream())); 
       // Eviga loopen 
       while (true) { 
        line = in.readLine(); 
        textArea.append(line + "\n"); 
       } 
      } catch (IOException e) { 
       // Ignorera. 
      } 
     } 

    }); 

    // Vad som händer när man trycker på enter i JTextField 
    private AbstractAction onEnterPressAction = new AbstractAction() { 
     @Override 
     public void actionPerformed(ActionEvent e) { 
      String textToSend = inputTextField.getText(); 
      inputTextField.setText(""); 
      try { 
       // Skickar ut strängen till servern. 
       out = new PrintWriter(new OutputStreamWriter(socket.getOutputStream(), "ISO-8859-1"), true); 
       out.write(textToSend + "\n"); 
       out.flush(); 
       // Om man skriver /quit. Stänger allt. 
       if (textToSend.startsWith("/quit")) { 
        if (out != null) out.close(); 
        if (in != null) in.close(); 
        if (socket != null) socket.close(); 
        System.exit(0); 
       } 
      } catch (IOException e1) { 
       e1.printStackTrace(); 
      } 
     } 
    }; 

    public Client(String host, int port) { 
     // Konstruktorn. 
     // Skapar en Socket. 
     try { 
      socket = new Socket(host, port); 
     } catch (IOException e) { 
      // Kunde inte kopplas till servern. 
      JOptionPane.showMessageDialog(null, "Could not connect to server", "Connection Error", JOptionPane.ERROR_MESSAGE); 
      System.exit(0); 
     } 

     // Sätter titelrad. 
     setTitle("CONNECTED TO SERVER: " + host + " IN PORT: " + port); 

     // Startar eviga läsloopen. 
     backgroundThread.start(); 

     // Tar hand om JFrame. 
     setLayout(new BorderLayout()); 
     setVisible(true); 
     setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); 

     // Södra JPanel (JTextField som man skriver meddelande i) 
     JPanel southPanel = new JPanel(); 
     southPanel.add(inputTextField); 
     inputTextField.addActionListener(onEnterPressAction); 

     // Centrala JPanel (JTextArea som visar alla meddelanden). 
     JPanel centerPanel = new JPanel(); 
     JScrollPane scrollPane = new JScrollPane(textArea); 
     centerPanel.add(scrollPane); 

     // Adderar och packar. 
     add(centerPanel, BorderLayout.CENTER); 
     add(southPanel, BorderLayout.SOUTH); 
     pack(); 
     setLocationRelativeTo(null); 

    } 

    public static void main(String[] args) { 
     if (args.length == 0) { 
      // Skickar default värden. 
      new Client("127.0.0.1", 2000); 
     } else if (args.length == 1) { 
      // Skickar argument och default port värde. 
      new Client(args[0], 2000); 
     } else if (args.length == 2) { 
      // Skickar argumenter. 
      new Client(args[0], Integer.parseInt(args[1])); 
     } 
//  new Client("192.168.1.66", 2000); 
    } 
} 

最後,這裏的錯誤我儘快得到我關閉客戶端(如按X按鈕或鍵入/退出):

Exception in thread "Thread-2" java.util.ConcurrentModificationException 
    at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901) 
    at java.util.ArrayList$Itr.next(ArrayList.java:851) 
    at com.company.ClientThread.run(Server.java:186) 

回答

0
   Iterator i = threads.listIterator(); 
       while (i.hasNext()) { 
        ClientThread c = (ClientThread) i.next(); 
        if (c == this) { 
         c.interrupt(); 
         i.remove(); 
         frame.changeTitle(); 
        } 
       } 

使用Iterator刪除元素。

如果在創建迭代器後修改列表,則會拋出異常。

+0

謝謝,就是這樣! –

相關問題