Java UDP - сервер, принимающий пакеты на несколько портов - PullRequest
0 голосов
/ 29 декабря 2018

У меня здесь довольно сложный вопрос, надеюсь, понимание моего кода не будет такой проблемой.

Я пишу программу, основанную на связи UDP и TCP в Java.Сервер прослушивает несколько портов UDP (1. количество и количество портов задается пользователем в параметрах программы; 2. для каждого порта создается поток) для пакетов от клиентов (может быть несколько попыток отправитьпакет на сервер).Каждый пакет содержит идентификатор Клиента, сообщение и номер порта UDP Клиента, который отправил этот пакет.Сервер получает пакет, помещает сообщение в HashMap (идентификатор клиента является ключом, отправленные сообщения хранятся в списке строк).Для каждого полученного пакета Сервер проверяет список строк, соответствуют ли сообщения, отправленные с указанного клиента, паролю.Если сообщения находятся в правильном порядке, Сервер отправляет сгенерированный номер порта для связи TCP с Клиентом, который отправил правильный пароль, открывает ServerSockets, они выполняют простую связь, и Клиент закрывается.

Теперь Клиентдолжен иметь возможность отправлять свои сообщения на различные порты.Например, сервер прослушивает порты 2000 и 3000. Клиент должен иметь возможность отправлять 2 сообщения на порт 2000 и еще два сообщения на порт 3000. Однако сервер, похоже, получает сообщения только на первый открытый порт.

Если клиент отправляет все свои сообщения на один порт, все работает нормально.

Вот класс сервера:

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 голосов
/ 29 декабря 2018

Ваш Runnable зацикливается навсегда после получения блокировки на общем объекте с именем packetsReceived, так что только один из ваших потоков действительно сможет выполнить что-либо;другой поток будет ожидать блокировки навсегда.

Вы сможете проверить это с помощью простого дампа потока, пока выполняется пример программы.

Решение (проблемы синхронизации) состоит в том,чтобы получить эту блокировку только тогда, когда вы действительно хотите изменить HashMap.Итак, удалите synchronized(packetsReceived) вне цикла и поместите его вокруг кода, который фактически выполняет вызовы containsKey / remove / put для HashMap.

...