我的UDP客户端应用程序逻辑出现问题

时间:2016-04-14 14:15:00

标签: java multithreading udp

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

  1. 服务器只接受UDP数据报,它是非常特定的消息格式。
  2. 服务器是基于订阅的。它只会在订阅特定消息协议(例如,聊天,时间,温度,频道等)时发送客户端消息。
  3. 当您订阅服务时,您必须向服务器发送永久UDP侦听端口,以便它可以向您发送订阅的消息。如果你不听那个端口,你就不会收到你的消息。
  4. 您可以向服务器询问相关信息(服务器状态,消息状态等),但如果您要求太多,则会断开您的连接。
  5. 前面提到的信息不包括客户端到客户端的消息。这些将在收到时由服务器发送出去。客户端订阅消息协议后不必询问消息。
  6. 客户端必须向服务器发送常规心跳消息,以使服务器知道您仍对其服务感兴趣。如果心跳频繁发送,服务器将断开您的连接。
  7. 服务器还发送常规心跳,让客户知道它仍在运行。
  8. 在启动时,我发送此服务器为空服务订阅,只是为了进入其订阅者列表(根据服务器规则这是正确的)。

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

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

    最后,我向服务器发送订阅请求,以获取我希望收到的有关服务的消息。

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

    我认为这是因为我的监听器线程正在阻塞,等待永远不会发生的消息。如果我首先启动我的应用程序,我的服务请求就会消失,但服务器永远不会收到请求。然后我开始听,但服务器永远不会跟我说话,因为(根据服务器)我没有订阅。我不能经常发送订阅请求,否则服务器将断开我的连接。服务器文档说,如果错过了两个服务器心跳,那么客户端应该重新订阅。但是,我无法解决如何做到这一点。这就是我所拥有的。我已经指出我认为“麻烦”的地方是评论“这是我的监听器阻止的地方,等待来自服务器的消息。”。

    主要

    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;
        }
    
    }
    

    那么,有关如何解决这个问题的任何建议吗?

    谢谢!

1 个答案:

答案 0 :(得分:0)

myDatagramSocket.receive(myDatagramPacket);

来自文档:

  

此方法将一直阻塞,直到收到数据报。

如果您从未在服务器上注册,则不会收到消息,并且线程会一直阻塞。

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

  

setSoTimeout

此外,您可能需要在断开循环后设置 run = false; ,因为此时您还没有收听。