Java 我的UDP客户端应用程序逻辑有问题

Java 我的UDP客户端应用程序逻辑有问题,java,multithreading,udp,Java,Multithreading,Udp,我正在开发一个通过UDP与服务器通信的Java应用程序。这是一个特定的服务器,因为它所做的只是通过网络将“消息”从一个客户机中继到另一个客户机,消息格式为非常特定的消息格式。消息格式小且紧凑,可利用极小的带宽。此服务器有独特的规则: 服务器只接受特定消息格式的UDP数据报 服务器是基于订阅的。仅当客户端订阅了特定的消息协议(如聊天、时间、温度、频道等)时,服务器才会发送客户端消息 订阅服务时,必须向服务器发送一个永久UDP侦听端口,以便服务器可以向您发送订阅的邮件。如果您不在该端口上侦听,则无法

我正在开发一个通过UDP与服务器通信的Java应用程序。这是一个特定的服务器,因为它所做的只是通过网络将“消息”从一个客户机中继到另一个客户机,消息格式为非常特定的消息格式。消息格式小且紧凑,可利用极小的带宽。此服务器有独特的规则:

  • 服务器只接受特定消息格式的UDP数据报
  • 服务器是基于订阅的。仅当客户端订阅了特定的消息协议(如聊天、时间、温度、频道等)时,服务器才会发送客户端消息
  • 订阅服务时,必须向服务器发送一个永久UDP侦听端口,以便服务器可以向您发送订阅的邮件。如果您不在该端口上侦听,则无法收到邮件
  • 您可以向服务器询问信息(服务器状态、消息状态等),但如果您要求太多,它将断开您的连接
  • 前面提到的信息不包括客户端到客户端的消息。服务器在收到消息时会发送这些消息。客户端订阅消息协议后不必请求消息
  • 客户端必须定期向服务器发送心跳消息,让服务器知道您仍然对其服务感兴趣。如果心跳发送得太频繁,服务器将断开您的连接
  • 服务器也会定期发送心跳信号,让客户端知道它仍在运行
  • 在启动时,我向该服务器发送一个空的服务订阅,只是为了进入其订阅服务器列表(根据服务器规则,这是正确的)

    在收到初始消息后,我开始发送心跳,每5秒发送一次(根据服务器规则,这是正确的)

    此时,我启动一个线程来侦听来自服务器的消息

    最后,我向服务器发送一个订阅请求,请求接收有关服务的消息

    在这一点上,我遇到了问题。如果我的应用程序在服务器应用程序运行之前启动,或者如果服务器应用程序在运行时重新启动,我不会从服务器收到任何消息

    我认为这是因为我的侦听器线程正在阻塞,等待永远不会出现的消息。如果我首先启动应用程序,我的服务请求将发出,但服务器从未收到请求。然后我开始侦听,但服务器将永远不会与我对话,因为(根据服务器)我还没有订阅。我无法不断发送订阅请求,否则服务器会断开我的连接。服务器文档中说,如果错过两个服务器心跳,那么客户端应该重新订阅。但是,我无法思考如何执行此操作。以下是我所拥有的。我已经指出了我认为存在“问题”的地方带有注释“此处是我的侦听器正在阻止的位置,正在等待来自服务器的消息。”

    Main

    public static void main(String[] args){
        SubscribeToServices mySubscriptions = new SubscribeToServices();
        mySubscriptions.subscribe(0); // 0 means no services
    
        Heartbeat myHeartbeat = new Heartbeat();
        Thread heartbeatThread = new Thread(myHeartbeat);
        heartbeatThread.start();
    
        MyListener listener = new MyListener();
        Thread listenerThread = new Thread(listener);
        listenerThread.start();
    
        mySubscriptions.subscribe(1234); // a specific set of subscriptions
    }
    
    MyListener.java

    public class MyListener implements Runnable {
    
        private static final Logger LOG = Logger.getLogger(MyListener.class.getName());
    
        private volatile boolean run = true;
    
        private DatagramSocket myDatagramSocket;
        private DatagramPacket myDatagramPacket;
        private MessageHeader messageHeader;
        private int receiveBufferSize;
    
        private byte[] receiveBuffer;
        private int messageID;
    
        private Timer myTimer;
    
        @Override
        public void run() {
            try {
                // Create and bind a new DatagramSocket
                myDatagramSocket = new DatagramSocket(null);
                InetSocketAddress myInetSocketAddress = new InetSocketAddress(15347);
                myDatagramSocket.bind(myInetSocketAddress);
            } catch (SocketException se) {
                LOG.log(Level.SEVERE, se.getMessage());
                return;
            }
    
            // Set-up the receive buffer
            receiveBuffer = new byte[2047];
            myDatagramPacket = new DatagramPacket(receiveBuffer, 2047);
    
            while (run) {
                try {
                    // Receive the data from Server
                    // Here is where my listener is blocking, waiting for messages from the server.
                    myDatagramSocket.receive(myDatagramPacket);
                } catch (IOException ioe) {
                    LOG.log(Level.SEVERE, ioe.getMessage());
                    break;
                }
                byte[] data = myDatagramPacket.getData();
                receiveBufferSize = myDatagramPacket.getLength();
    
                // Extract the message header and ID
                messageHeader = ExtractMessageHeaders.extract(data, receiveBufferSize);
                messageID = (messageHeader.getMessageID() & 0xff);
    
                switch (messageID) {
                    // Do switch here ...
                    // Check for the heartbeat and other messages from the server.
                }
                myDatagramPacket.setLength(2047);
            }
        }
    
        public boolean isRun() {
            return run;
        }
    
        public void setRun(boolean run) {
            LOG.log(Level.INFO, "Changing the listening thread.");
            this.run = run;
        }
    
    }
    
    那么,有什么建议可以帮助我解决这个问题吗

    谢谢

    myDatagramSocket.receive(myDatagramPacket);
    
    从文档中:

    此方法会一直阻塞,直到收到数据报为止

    如果您从未在服务器上注册,您将不会收到消息,线程将继续阻塞

    在客户端实现超时将使您能够跟踪失败的注册尝试,例如通过

    设置超时


    此外,您可能需要设置run=false中断while循环后,因为此时您不再收听。

    UDP不可靠,所以我会分散订阅,但遵守服务器的防洪泛规则。这意味着我将在短时间内发送5个订阅,然后等待看是否从服务器得到答案,如果没有,然后扇出4在一个更宽的距离,然后3然后2然后1,然后我考虑服务器下来,放弃通知用户或处理这种情况,否则。一个问题:你对丢失的心跳有反应吗?你真的重新订阅了吗?不,我对丢失的心跳或重新订阅没有反应。我不知道我会/应该在哪里做这件事。你需要启动一个“heartbeatlistener”线程,它只监视来自服务器的心跳信号,并提供一种发出服务器中断信号的方法。您编写协议sais 2连续丢失的心跳将触发重新订阅。因此,您可以在监视器上注册某种监听器,当发生这种情况时,监听器将被通知(执行)。您可能需要实现“下侧翼触发”。也就是说,当心跳到达时,服务器将启动和运行,当两个时间帧没有心跳时,信号“服务器宕机”。什么使您认为IOExt会被抛出?因为这是监听传入消息的方式。如果服务器正在发送,但没有收到任何消息,则Receive未被执行。是。它会阻塞。但也不例外。在这里,阻塞是预期的行为,将导致正确接收消息。从OP提供的信息来看,就我所知,这是唯一一种不接收消息的方式。我删除了“中断”,但它仍然卡在同一个位置。