【问题标题】:Java UDP - server accepting packets on multiple portsJava UDP - 服务器在多个端口上接受数据包
【发布时间】:2019-05-26 23:36:04
【问题描述】:

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

我正在用 Java 编写一个基于 UDP 和 TCP 通信的程序。 服务器正在监听多个 UDP 端口(1. 端口的数量和数量由用户在程序参数中给出;2. 为每个端口创建一个线程)用于来自客户端的数据包(可能有多个尝试发送一次数据包到服务器)。每个数据包都包含客户端 ID、发送此数据包的客户端的消息和 UDP 端口号。服务器接收到数据包,将消息放入 HashMap 中(客户端 ID 是键,发送的消息存储在字符串列表中)。在收到每个数据包时,服务器检查字符串列表是否从指定客户端发送的消息与密码匹配。如果消息的顺序正确,Server 会发送一个生成的端口号用于与发送正确密码的 Client 进行 TCP 通信,打开 ServerSockets,它们执行简单的通信,然后 Client 关闭。

现在,客户端应该能够将他的消息发送到各个端口。例如,服务器正在侦听端口 2000 和 3000。客户端应该能够向端口 2000 发送 2 条消息,并在端口 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 中 - 情况更糟。

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

【问题讨论】:

    标签: java tcp udp port


    【解决方案1】:

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

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

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

    【讨论】:

    • 当我这样做的时候,所有的消息都进来了,但它们不是按顺序排列的。我试图让客户端每次发送消息时都休眠,以便服务器能够以正确的顺序接收数据包,但这会产生更多问题(当我使用 TimeUnit.SECONDS.sleep() 时,服务器会以某种方式削减 2从客户端传入的第一条消息)。
    • 你是如何确定收到消息的“顺序”的?
    猜你喜欢
    • 2012-05-03
    • 2014-08-18
    • 2012-05-28
    • 2017-10-13
    • 1970-01-01
    • 2015-05-28
    • 1970-01-01
    • 1970-01-01
    • 2021-05-03
    相关资源
    最近更新 更多