Java UDP-服务器在多个端口上接受数据包

时间:2018-12-29 14:34:39

标签: java tcp udp port

我在这里有一个非常复杂的问题,希望理解我的代码不会成为问题。

我正在用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中-更糟糕的是。

如果有任何建议和意见,我将不胜感激。

1 个答案:

答案 0 :(得分:0)

您的Runnable在获得对名为packetsReceived的共享对象的锁定后将永远循环,因此,只有一个线程实际上可以完成任何事情;另一个线程将永远等待锁定。

在示例程序运行时,您应该能够通过简单的线程转储来验证这一点。

(对于同步问题)解决方案是仅在您要实际修改HashMap时获得该锁。因此,从循环外部删除synchronized(packetsReceived),并将其放在实际执行containsKey上的remove / put / HashMap调用的代码周围。 / p>