UDP:跳过超时请求的响应

时间:2018-01-23 10:40:55

标签: java multithreading servlets udp semaphore

我有一个UDP进程需要定义接收端口和IP地址。 经过测试,我发现在请求的同时具有相同的接收IP和端口可能导致数据丢失/切换。 我想到如果现有的请求具有相同的数据,则传入/第二个请求将等待第一个请求完成事务,然后执行请求。 由于这是一个servlet,我同时担心不同浏览器中的多个请求,我使用Semaphore以单例模式返回单个实例。我的DatagramSocket也是单例模式,以避免"已经绑定"错误。 我的代码在一个快乐路径场景中没有数据切换的情况下表现良好,在这种情况下,它只会发送请求和响应而没有错误。 我设置套接字超时并执行超时异常情况,这是我得到的

第一次请求=====> UDP服务器

超时异常< ==== UDP服务器

第二次请求=====> UDP服务器

第一个响应< ==== UDP服务器

我在第二次请求时收到了第一个回复,依此类推。

如何拒绝/跳过超时请求的响应?请注意,我不允许根据要求在我的回复中添加标识符。

这是我的代码供您参考:

    private void calludp(String ip, String targetIp, String port, String targetPort, String timeout, byte[] message)
            throws IOException, SAXException, ParserConfigurationException, InterruptedException {

        String key = targetIp + ":" + targetPort;

        UDPReceptionData udpReceptionData = null;

        udpReceptionData = getReceptionLock(key, receptionMap);

        Semaphore semaphore = udpReceptionData.getSemaphore();

        DatagramSocket datagramSocket = udpReceptionData.getDatagramSocket();

        semaphore.acquire();

        try {

            send(ip, targetIp, port, targetPort, timeout, message, datagramSocket);

        } finally {
            semaphore.release();
        }
    }


    private void populateUDPReceptionMap(String port, String target) {

        if ((target != null && !target.isEmpty()) && (port != null && !port.isEmpty())) {

            Semaphore semaphore = new Semaphore(1);

            DatagramSocket datagramSocket = getSocketMap(port);

            UDPReceptionData udpReceptionData = new UDPReceptionData(datagramSocket, semaphore);

            this.receptionMap.put(target + ":" + port, udpReceptionData);

        }
    }

    private DatagramSocket getSocketMap(String port) {

        System.out.println(socketMap.toString());

        DatagramSocket datagramSocket = null;

        if (port != null && !port.isEmpty()) {

            if (!socketMap.containsKey(port)) {

                try {
                    datagramSocket = new DatagramSocket(Integer.parseInt(port));

                    this.socketMap.put(port, datagramSocket);
                } catch (NumberFormatException | SocketException e) {
                    e.printStackTrace();
                }

            } else {
                datagramSocket = socketMap.get(port);
            }
        }

        return datagramSocket;
    }

    public UDPReceptionData getReceptionLock(String ipPort, Map<String, UDPReceptionData> receptionMap) {
        System.out.println(receptionMap.toString());

        return receptionMap.get(ipPort);
    }

    public byte[] send(String target, String receptionTarget, String port, String receptionPort, String timeout,
            byte[] message, DatagramSocket clientSocket) throws IOException {

        String messageResponse = ""; // Message response to be return to the
                                     // caller

        int intTimeout = Integer.parseInt(timeout);

        DatagramPacket receivePacket = null;

        try {

            InetAddress ipAddress = InetAddress.getByName(target);

            int intPort = Integer.parseInt(port);

            byte[] receiveData = new byte[1024];

            DatagramPacket sendPacket = new DatagramPacket(message, message.length, ipAddress, intPort);

            clientSocket.send(sendPacket);

            // receive the data
            receivePacket = new DatagramPacket(receiveData, receiveData.length);

            try {
                clientSocket.setSoTimeout(intTimeout);

                clientSocket.receive(receivePacket);
            } catch (SocketTimeoutException e) {

                throw new SocketTimeoutException("Socket Timeout Exception");
            }

            messageResponse = new String(receivePacket.getData());

            String mes = new String(message, "UTF-8");

            System.out.println("FROM SERVER:" + messageResponse + " :::: " + mes);

        } finally {
            // commented out the close since this is a single instance DatagramSocket
            // clientSocket.close();
        }

        return receivePacket.getData();
    }

UDPReceptionData.java

    import java.net.DatagramSocket;
    import java.util.concurrent.Semaphore;

    public class UDPReceptionData {

        private Semaphore semaphore;

        private DatagramSocket datagramSocket;

        public UDPReceptionData(DatagramSocket datagramSocket, Semaphore semaphore) {

            this.datagramSocket = datagramSocket;

            this.semaphore = semaphore;

        }

        public DatagramSocket getDatagramSocket() {
            return this.datagramSocket;
        }

        public Semaphore getSemaphore() {
            return this.semaphore;
        }

    }

UDPServer.java

    private void udpSender(DatagramSocket serverSocket) throws IOException, InterruptedException {
            while (true) {

                byte[] responseMessage = new byte[1024];

                byte[] requestMessage = new byte[1024];

                DatagramPacket receivePacket = new DatagramPacket(responseMessage, responseMessage.length);

                serverSocket.receive(receivePacket);

                String receivedMessage = new String(receivePacket.getData());

                System.out.println("RECEIVED: " + receivedMessage);

                InetAddress IPAddress = receivePacket.getAddress();

                int port = receivePacket.getPort();

                requestMessage = receivedMessage.getBytes();

                // Set the sending packet to designated IP Address and port
                DatagramPacket sendPacket = new DatagramPacket(requestMessage, requestMessage.length, IPAddress, port);

                //delay for 4 seconds for timeout testing
                Thread.sleep(4000);

                serverSocket.send(sendPacket);
            }

        }

主要方法

public static void main(String[] args) {

    UDPProcess m = new UDPProcess();

    m.populateUDPReceptionMap("9090", "localhost");

    String request = "localhost:9090:message1";

    String request2 = "localhost:9090:message2";

    new Thread() {
        public void run() {
            try {

                m.calludp("localhost", "localhost", "7979", "9090", "2000", request.getBytes());

            } catch (IOException | SAXException | ParserConfigurationException | InterruptedException e) {
                e.printStackTrace();
            } 
        }
    }.start();

    new Thread() {

        public void run() {

            try {

                m.calludp("localhost", "localhost", "7979", "9090", "60000", request2.getBytes());

            } catch (IOException | SAXException | ParserConfigurationException | InterruptedException e) {
                e.printStackTrace();
            }
        }
    }.start();
}

结果:

    java.net.SocketTimeoutException: Socket Timeout Exception
        at com.comp.proj.connector.UDPProcess.send(UDPProcess.java:221)
        at com.comp.proj.connector.UDPProcess.calludp(UDPProcess.java:135)
        at com.comp.proj.connector.UDPProcess.access$0(UDPProcess.java:118)
        at com.comp.proj.connector.UDPProcess$1.run(UDPProcess.java:51)
    FROM SERVER:localhost:9090:message2 :::: localhost:9090:message1

1 个答案:

答案 0 :(得分:0)

您的请求和回复中需要序列号。您需要忽略先前序列号的响应。即使没有超时也可能发生这种情况,因为UDP数据报可以复制(或更多)。