我制作了一个应用程序,这是一个非常简单的聊天功能Server
和Client
。聊天功能完全正常,但只要我关闭Client
,我就会在ConcurrentModificationException
中获得Server
。应用程序仍然正常运行,但我知道我不应该收到该错误。我无法弄清楚为什么会这样。
这是我的服务器:
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 :(得分:0)
Iterator i = threads.listIterator();
while (i.hasNext()) {
ClientThread c = (ClientThread) i.next();
if (c == this) {
c.interrupt();
i.remove();
frame.changeTitle();
}
}
使用Iterator删除元素。
如果在创建迭代器后修改列表,则会抛出异常。