我在这里有一个非常复杂的问题,希望理解我的代码不会成为问题。
我正在用Java编写基于UDP和TCP通信的程序。 服务器正在侦听多个UDP端口(1.端口的数量和数量由用户在程序参数中指定; 2。每个端口都会创建一个线程),以接收来自客户端的数据包(可能有多个尝试发送一个一次发送到服务器)。每个数据包均包含客户端ID,发送此数据包的客户端的消息和UDP端口号。服务器接收数据包,将消息放入HashMap(客户端ID是密钥,发送的消息存储在字符串列表中)。在收到的每个数据包上,服务器都会检查字符串列表,从指定客户端发送的消息是否与密码匹配。如果消息顺序正确,则服务器将发送生成的端口号,用于与发送正确密码的客户端进行TCP通信,打开ServerSockets,执行简单的通信,然后客户端关闭。
现在,客户端应该能够将其消息发送到各个端口。例如,服务器在端口2000和3000上侦听。客户端应该能够向端口2000发送2条消息,在端口3000上发送另外两条消息。但是,服务器似乎仅在第一个打开的端口上接收消息。 / p>
如果客户端在一个端口上发送所有消息,则一切正常。
这是服务器类:
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
public class Server {
static int portsOpenedQuantity;
static HashMap<String, List<String>> packetsReceived = new HashMap<>();
static List<Integer> portsTCP = new ArrayList<>();
public static void main(String[] args) {
portsOpenedQuantity = args.length;
List<String> listOfPorts = new ArrayList<>();
for(int i = 0; i < portsOpenedQuantity; ++i)
if(!listOfPorts.contains(args[i]) && (Integer.parseInt(args[i]) > 1024))
listOfPorts.add(args[i]);
for(int i = 0; i < listOfPorts.size(); ++i) {
final int j = i;
System.out.println("SERVER listening on port: " + listOfPorts.get(j));
Thread listeningPort = new Thread(new Runnable() {
@Override
public void run() {
synchronized (packetsReceived){
try {
byte[] packetReceived = new byte[256];
DatagramSocket ds = new DatagramSocket(Integer.parseInt(listOfPorts.get(j)));
DatagramPacket dp = new DatagramPacket(packetReceived, 256);
while(true){
ds.receive(dp);
List<String> sequence = new ArrayList<>();
System.out.println("SERVER received a packet");
String msgReceived = new String(dp.getData(), 0, dp.getLength());
String[] separatedMsg = msgReceived.split(" ");
int portUDPNumber = Integer.parseInt(separatedMsg[2]);
System.out.println("Id: " + separatedMsg[0]);
System.out.println("Value: " + separatedMsg[1]);
System.out.println("Port UDP: " + separatedMsg[2]);
if(packetsReceived.containsKey(separatedMsg[0])) {
sequence = packetsReceived.get(separatedMsg[0]);
packetsReceived.remove(separatedMsg[0]);
System.out.println(separatedMsg[1]);
sequence.add(separatedMsg[1]);
System.out.println(sequence);
packetsReceived.put(separatedMsg[0], sequence);
} else {
System.out.println(sequence);
sequence.add(separatedMsg[1]);
packetsReceived.put(separatedMsg[0], sequence);
}
String sequenceResult = "";
for(int k = 0; k < sequence.size(); ++k) {
sequenceResult += sequence.get(k);
}
System.out.println(sequenceResult);
if(sequenceResult.equals("!@#$")){
System.out.println("Connecting via TCP...");
int portNumber = (int)((Math.random()*100)+5000);
boolean portAvailable = true;
ServerSocket ss = null;
System.out.println("TCP port number: " + portNumber);
while(portAvailable) {
try{
ss = new ServerSocket(portNumber);
portsTCP.add(portNumber);
portAvailable = false;
} catch(Exception e) {
portAvailable = true;
portNumber++;
}
}
System.out.println("socket number aquired");
String portNr = portNumber+"";
byte[] portNrToSend = portNr.getBytes();
dp = new DatagramPacket(portNrToSend, portNrToSend.length, InetAddress.getByName("localhost"), portUDPNumber);
System.out.println("Datagram created");
ds.send(dp);
System.out.println("Datagram sent");
Socket s = ss.accept();
System.out.println("Port number sent to: " + portUDPNumber);
PrintWriter out = new PrintWriter(s.getOutputStream(), true);
BufferedReader in = new BufferedReader(new InputStreamReader(s.getInputStream()));
String msgFromClient = in.readLine();
System.out.println("Message from client: " + msgFromClient);
out.println("I received your message");
in.close();
out.close();
s.close();
ss.close();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
});
listeningPort.start();
}
}
}
和客户端类:
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
public class Client {
InetAddress ip;
String idClient;
List<Integer> portsUDP = new ArrayList<>();
String sequence;
public Client(String[] args) {
try {
ip = InetAddress.getByName(args[0]);
idClient = args[1];
for(int i = 2; i < args.length; ++i)
portsUDP.add(Integer.parseInt(args[i]));
this.sequence = "!@#$";
DatagramSocket ds = new DatagramSocket();
DatagramPacket dp = null;
for(int i = 0; i < portsUDP.size(); ++i) {
byte[] toSend = new byte[256];
String msgToSend = idClient + " " + sequence.charAt(i) + " " + ds.getLocalPort();
System.out.println("CLIENT named as: " + idClient + " sends a message: " + sequence.charAt(i) + " " + ds.getLocalPort());
toSend = msgToSend.getBytes();
dp = new DatagramPacket(toSend, toSend.length, ip, portsUDP.get(i));
ds.send(dp);
System.out.println("CLIENT: " + idClient + " sent a packet");
toSend = new byte[256];
msgToSend = "";
}
String received;
byte[] tabReceived = new byte[256];
dp = new DatagramPacket(tabReceived, tabReceived.length);
System.out.println("Datagram created");
ds.receive(dp);
System.out.println("Datagram received");
received = new String(dp.getData(), 0, dp.getLength());
System.out.println("Received TCP port number: " + received);
int portTCP = Integer.parseInt(received);
int portNumber = (int)((Math.random()*100)+5000);
boolean portAvailable = true;
ServerSocket ss = null;
while(portAvailable) {
try{
ss = new ServerSocket(portNumber);
portAvailable = false;
} catch(Exception e) {
portAvailable = true;
portNumber++;
}
}
System.out.println("ServerSocket created");
Socket s = new Socket(ip, portTCP);
System.out.println("Socket created");
PrintWriter out = new PrintWriter(s.getOutputStream(), true);
BufferedReader in = new BufferedReader(new InputStreamReader(s.getInputStream()));
out.println("Succes of communication");
String msgFromServer = in.readLine();
System.out.println("Message from SERVER: " + msgFromServer);
in.close();
out.close();
s.close();
ss.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
new Client(args);
}
}
我认为在这里造成问题的是HashMap上的同步,但是当我在没有同步的情况下运行它时,数据包会以完全随机的顺序进入,无法正确存储在HashMap中-更糟糕的是。
如果有任何建议和意见,我将不胜感激。
答案 0 :(得分:0)
您的Runnable
在获得对名为packetsReceived
的共享对象的锁定后将永远循环,因此,只有一个线程实际上可以完成任何事情;另一个线程将永远等待锁定。
在示例程序运行时,您应该能够通过简单的线程转储来验证这一点。
(对于同步问题)解决方案是仅在您要实际修改HashMap
时获得该锁。因此,从循环外部删除synchronized(packetsReceived)
,并将其放在实际执行containsKey
上的remove
/ put
/ HashMap
调用的代码周围。 / p>