我有一个联网程序,在该程序中,我尝试从客户端获取响应并将其发送到服务器,然后将其发送到另一个特定的客户端。最后一步是发生问题的地方。我尝试通过将所有客户端添加到哈希图中并从那里获取它们来获取客户端,但是我从那里获得了ConcurrentModificationException。下面是我的代码:
服务器:
public class Server {
public static void main(String[] args) {
new Server();
}
ServerSocket serverSocket = null;
static int port = 3339;
static Map<String, Socket> clients = new HashMap<String, Socket>();
private int clientCounter = 0;
public Server() {
try {
serverSocket = new ServerSocket(port);
System.out.println("[SERVER] Server successfully launched on port " + port);
} catch (IOException e) {
System.out.println("[ERROR] Unable to launch server on port " + port);
}
while(true) {
try {
Socket socket = serverSocket.accept();
clientCounter++;
ClientThread client = new ClientThread("Client " + clientCounter, socket);
System.out.println("[SERVER] New client connected: " + client.getClientName() + " (ip:" + socket.getInetAddress() + " port:"
+ socket.getPort() + " localPort:" + socket.getLocalPort() + ")");
clients.put(client.getClientName(), socket);
Thread clientThread = new Thread(client);
clientThread.start();
} catch (IOException e) {
System.out.println("[ERROR] Unable to accept request from client");
}
}
}
}
ClientThread类(用于多线程)
public class ClientThread implements Runnable{
private String clientName;
private Socket socket;
public ClientThread(String clientName, Socket socket) {
this.socket = socket;
this.clientName = clientName;
}
@Override
public void run() {
/**
* The while(true) loop makes it possible to send multiple responses from client to server back and forth
* without it could only make 1 request because of socket.accept() method
*/
while(true) {
Server.clients.forEach((string, socket) -> {
String message = null;
if(string.equals("Client 1")) {
try {
DataInputStream inputStream1 = new DataInputStream(socket.getInputStream());
DataOutputStream outputStream1 = new DataOutputStream(socket.getOutputStream());
message = inputStream1.readUTF();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(string.equals("Client 2")) {
try {
DataInputStream inputStream2 = new DataInputStream(socket.getInputStream());
DataOutputStream outputStream2 = new DataOutputStream(socket.getOutputStream());
outputStream2.writeUTF(message);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
}
}
public String getClientName() {
return clientName;
}
}
客户:
public class Client {
public static void main(String[] args) {
new Client();
}
public Client() {
Socket socket = null;
try {
socket = new Socket("127.0.0.1", Server.port);
} catch (UnknownHostException e) {
System.out.println("[ERROR] Error connecting to unknown ip adress");
} catch (IOException e) {
System.out.println("[ERROR] Error connecting to server");
}
try {
while(true) {
System.out.println("You can start typing:");
DataInputStream inputStream = new DataInputStream(socket.getInputStream());
DataOutputStream outputStream = new DataOutputStream(socket.getOutputStream());
Scanner scanner = new Scanner(System.in);
String message;
while(scanner.hasNextLine()) {
message = scanner.nextLine();
if(message.equalsIgnoreCase("quit")) {
socket.close();
break;
}
outputStream.writeUTF(message);
//reading messages from server
String received = inputStream.readUTF();
System.out.println(received);
}
}
} catch (IOException e) {
System.out.println("[ERROR] Unable to get streams from server");
}
}
}
任何需要帮助的人。
答案 0 :(得分:1)
简短的答案...您可以使用ConcurrentHashMap类摆脱此异常。这将解决问题,我仍然认为您应该重构代码。
更长的答案。请参阅Java文档https://docs.oracle.com/javase/8/docs/api/java/util/HashMap.html
此类的所有“集合视图方法”返回的迭代器都是快速失败的:如果在创建迭代器后的任何时间对结构进行结构修改,则除了通过迭代器自己的remove方法之外,该迭代器将以任何方式进行迭代抛出ConcurrentModificationException。因此,面对并发修改,迭代器会快速干净地失败,而不会在未来的不确定时间内冒任意,不确定的行为的风险。
您正在启动使用迭代器(即Server.clients.forEach)的线程(即ClientThread)。如果在接受下一个套接字时此迭代器仍处于活动状态,则将出现此异常。
在服务器类中,您可能希望每个ClientThread具有一个套接字连接。这是有道理的,因为您要为每个连接创建一个新的线程,这意味着您不需要遍历连接。