英文:
Errors when trying to send message from client to another client
问题
以下是您提供的代码的中文翻译:
Server(服务器):
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("[服务器] 服务器成功启动,端口号:" + port);
} catch (IOException e) {
System.out.println("[错误] 无法在端口 " + port + " 上启动服务器");
}
while (true) {
try {
Socket socket = serverSocket.accept();
clientCounter++;
ClientThread client = new ClientThread("客户端 " + clientCounter, socket);
System.out.println("[服务器] 新客户端连接:" + client.getClientName() + " (IP:" + socket.getInetAddress() + " 端口:" +
socket.getPort() + " 本地端口:" + socket.getLocalPort() + ")");
clients.put(client.getClientName(), socket);
Thread clientThread = new Thread(client);
clientThread.start();
} catch (IOException e) {
System.out.println("[错误] 无法接受来自客户端的请求");
}
}
}
}
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() {
while (true) {
Server.clients.forEach((string, socket) -> {
String message = null;
if (string.equals("客户端 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("客户端 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;
}
}
Client(客户端):
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("[错误] 连接未知 IP 地址时出错");
} catch (IOException e) {
System.out.println("[错误] 连接服务器时出错");
}
try {
while (true) {
System.out.println("您可以开始输入:");
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);
// 从服务器读取消息
String received = inputStream.readUTF();
System.out.println(received);
}
}
} catch (IOException e) {
System.out.println("[错误] 无法从服务器获取流");
}
}
}
如有需要,您可以随时提问。
英文:
I have a networking program where i try to get the response from a client and send it over to the server where it should be send to another specific client. On the last step is where the problem occurs. I try to get the client by adding all clients to a hashmap and getting them from there but i get a ConcurrentModificationException from that. Below is my code:
Server:
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 class (used for multithreading)
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;
}
}
Client:
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");
}
}
}
Any help very appriciated.
答案1
得分: 1
短答案是... 你可以使用 ConcurrentHashMap 类来消除这个异常。这将解决这个问题,我仍然认为你应该重构代码。
长一点的答案。参见 Java 文档 https://docs.oracle.com/javase/8/docs/api/java/util/HashMap.html
> 由这个类的所有“集合视图方法”返回的迭代器都是快速失败的:如果在迭代器被创建后的任何时候,以任何方式(通过迭代器自己的 remove 方法以外的任何方式)对地图进行了结构性修改,迭代器都将抛出 ConcurrentModificationException。因此,面对并发修改,迭代器会迅速而干净地失败,而不会在将来的某个不确定的时间冒险地产生任意的、非确定性的行为。
你正在启动一个线程(即 ClientThread),该线程使用一个迭代器(即 Server.clients.forEach)。如果这个迭代器在下一个套接字被接受时仍然活动,你将会得到这个异常。
在服务器类中,你可能希望每个 ClientThread 拥有一个套接字连接。这是有道理的,因为你为每个连接创建了一个新的线程,这意味着你不需要遍历连接。
英文:
The short answer... you can get rid of this Exception by using ConcurrentHashMap class. This will fix the problem, I still think you should refactor the code.
The longer answer. See the Java Docs https://docs.oracle.com/javase/8/docs/api/java/util/HashMap.html
> The iterators returned by all of this class's "collection view methods" are fail-fast: if the map is structurally modified at any time after the iterator is created, in any way except through the iterator's own remove method, the iterator will throw a ConcurrentModificationException. Thus, in the face of concurrent modification, the iterator fails quickly and cleanly, rather than risking arbitrary, non-deterministic behavior at an undetermined time in the future.
You are starting a thread (i.e. ClientThread) which uses an iterator (i.e. Server.clients.forEach). If this iterator is still active when the next socket is accepted, you will get this exception.
In the server class, you might want to have one socket connection per ClientThread. This would make sense since you are creating a new Thread for each connection, and it would mean you don't need to loop through the connections.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论