2016-04-14 41 views
0

我正在研究通过UDP与服务器通信的Java应用程序。这是一个特定的服务器,因为它所做的只是将消息“从一个客户端传递到另一个客户端,通过网络传输,具有特定的消息格式。该消息格式小而紧凑,以利用极小的带宽。有独特的规则此服务器:我遇到了UDP客户端应用程序的逻辑问题

  1. 服务器只接受UDP数据报中它是非常具体的消息格式
  2. 服务器是基于订阅它将只发送客户端的消息,如果他们预订了特定的。消息协议(例如,聊天,时间,温度,频道等)
  3. 当你订阅服务时,你必须向服务器发送一个烫发邮件一个UDP的侦听端口,所以它可以发送给你你的订阅消息。如果你不听那个端口,你将不会收到你的消息。
  4. 您可以向服务器询问有关信息(服务器状态,消息状态等),但如果问得太多,则会断开与您的连接。
  5. 前面提到的信息不包括客户端到客户端的消息。这些将在服务器收到时发送出去。一旦订阅了消息协议,客户端就不需要询问消息。
  6. 客户端必须定期发送心跳消息到服务器,让服务器知道您仍然对其服务感兴趣。如果心跳过于频繁,服务器将与您断开连接。
  7. 服务器还发出定期检测信号,让客户知道它仍在运行。

在启动时,我向该服务器发送一个空服务订阅,以获取其订阅者列表(根据服务器规则正确)。

之后,我开始发送心跳,每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; 
    } 

} 

所以,我如何能解决这个得到什么建议?

谢谢!

+0

UDP是不可靠的,所以我会扇出订阅,但服从服务器的反泛滥规则。这意味着我会在短时间内发送5个订阅,然后等待,看看我是否能够从服务器获得答案,如果没有,然后在更广的距离内展开4,然后3然后2然后1,最后我认为服务器放弃并放弃通知用户或处理这种情况。一个问题:你对失踪的心跳有反应吗?你真的重新订阅吗? – Fildor

+0

不,我不会对丢失的心跳或重新订阅作出反应。我无法弄清楚我会/应该这样做。 –

+0

您需要启动一个“heartbeatlistener”线程,该线程除监视从服务器进入的心跳信号并提供信号服务器停机信号的方法外别无其他。您编写协议SAIS 2缺少连续的心跳应触发重新订阅。所以你会在监视器上注册一些监听器,当监听器发生这种情况时会被通知(执行)。你可能想要实现“侧翼触发”。那就是当心跳到达时,你会考虑服务器启动并运行,并在没有心跳的两个时间范围内通过时发出“服务器关闭”信号。 – Fildor

回答

0
myDatagramSocket.receive(myDatagramPacket); 

从文档:

此方法一直阻塞数据报被接收。

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

实现对客户方超时将使您通过

setSoTimeout保持注册失败尝试的跟踪,例如

此外,您可能要设置运行= FALSE;在打断你的while循环之后,因为你现在不再在听这个。

+0

是什么让你认为会抛出IOException? – Fildor

+0

就我所了解的问题而言,调用接收方法是OP试图实现的目标,因为这是聆听传入消息的方式。如果服务器正在发送,并且没有进入,则接收不会被执行。 – Raijen

+0

是的。它阻止。虽然没有例外。 – Fildor