2015-02-13 2 views
1

У меня проблема, которую я не могу решить самостоятельно. Я думаю, что мой подход с разбиением и добавлением в список массивов и повторной сборкой фрагментов сообщения отлично работает, если (1) скорость сообщения MSG> BUFFER & составляет 1 мс/секунду. Но проблемы возникают, когда я отправляю более 1 сообщения/секунду, и мне нужно разделить большое/маленькое сообщение. Да, подход может быть неэффективным в долгосрочной перспективе, но это назначение, поэтому я просто хочу, чтобы он работал так, как я хотел, и я в порядке.UDP Отправка нескольких разделенных строк

Уверен, что проблема в том, что она, конечно, отправляет каждую часть сообщения. И выход такой же в моей консоли:

-------------------------------- 
| UDP Echo Client 
| Configuration: 
| server name: localhost 
| port: 4950 
| buffer: 8 
| rate: 5 
| message size: 15 
-------------------------------- 
Original: [HelloHe, lloHell, o] 
Received: [HelloHe] 
MESSAGE IS NOT EQUAL! 
Received: [HelloHe, HelloHe] 
MESSAGE IS NOT EQUAL! 
Received: [HelloHe, HelloHe, HelloHe] 
MESSAGE IS NOT EQUAL! 

Может кто-нибудь попробует мне помочь? Каков наилучший способ исправить это?

UDP Клиент:

import java.io.IOException; 
import java.net.*; 
import java.util.*; 

/* 
UDP Echo client. Sends a echo message of a size to the server and gets it back. 
It checks so that the message wasn't lost or anything has happened to it. 
by jv222dp 

Rate works perfectly when MSG.length <= MY_BUFFER. 
When the BUFFER is smaller then the MSG it works great if rate is 1 
*/ 
public class UDPEchoClient { 

    private static final String MSG = "HelloHelloHello"; 

    private static int MY_PORT; 
    private static int RATE; 
    private static int MY_BUFFER; 
    private static String HOST_NAME; 
    private static byte[] buf; 
    private static int packages; 
    private static int chars; 
    private static List<String> originalMsg; 
    private static List<String> receivedString = new ArrayList<>(packages); 
    private static DatagramPacket sendPacket; 

    public static void main(String[] args) { 

     if (!isCorrect(args)) { 
      System.exit(1); 
     } else { 

      try { 

      /* Configuration printout */ 
       System.out.println("--------------------------------" + 
         "\n| UDP Echo Client" + 
         "\n| Configuration: " + 
         "\n| server name: " + HOST_NAME + 
         "\n| port: " + MY_PORT + 
         "\n| buffer: " + MY_BUFFER + 
         "\n| rate: " + RATE + 
         "\n| message size: "+MSG.length()+ 
         "\n--------------------------------"); 
       /* Sets the buffer */ 
       buf = new byte[MY_BUFFER]; 

       /* Create socket */ 
       DatagramSocket socket = new DatagramSocket(null); 

       /* Create local endpoint using bind() */ 
       SocketAddress localBindPoint = new InetSocketAddress(0); 
       socket.bind(localBindPoint); 

       socket.setSoTimeout(2000); 

       /* Create remote endpoint */ 
       SocketAddress remoteBindPoint = new InetSocketAddress(HOST_NAME, 
         (MY_PORT)); 



       /* Sends and reads the echo message */ 
       sendEchoPackets(socket, remoteBindPoint); 


      } catch (SocketException se) { 
       System.err.println("Host unreachable!" + 
         "Wrong port or host offline"); 
      } 
     } 
    } 

    public static void sendEchoPackets(DatagramSocket socket, SocketAddress remoteBindPoint) { 

     System.out.println("Original: "+originalMsg.toString()); 

     /* For each string in the List of message parts */ 
     for (String message : originalMsg) { 

     /* Create datagram packet for sending message */ 
      sendPacket = new DatagramPacket(
        message.getBytes(), 
        message.length(), 
        remoteBindPoint); 

      Timer timer = new Timer(); 
      TimerTask rate = new TimerTask() { 

       @Override 
       public void run() { 
        try { 

         if (RATE == 0 || RATE == 1) { 
          for (int i = 0; i < RATE; i++) { 
           socket.send(sendPacket); 
           timer.cancel(); 
          } 
         } else { 
          for (int i = 0; i < RATE; i++) { 
           socket.send(sendPacket); 
           timer.cancel(); 
          } 

         } 
        } catch (IOException e) { 
         System.out.println(e.getMessage()); 
        } 
       } 
      }; 

      timer.scheduleAtFixedRate(rate, 0, 1000); 
      readEchoPacket(socket); 
     } 

    } 

    public static void readEchoPacket(DatagramSocket socket){ 

     try { 

      /* Create datagram packet for receiving echoed message */ 
      DatagramPacket receivePacket = new DatagramPacket(buf, buf.length); 

      socket.receive(receivePacket); 

      String receivedEcho = new String(
        receivePacket.getData(), 
        receivePacket.getOffset(), 
        receivePacket.getLength()); 

      receivedString.add(receivedEcho); 

      /* Compares if the message is the same as the one that was sent */ 
      compareEchoMessage(receivedString); 
     } 
     catch (IOException e) { 
      System.out.println(e.getMessage()); 
     } 


    } 

    public static void compareEchoMessage(List<String> receivedMsg){ 
     StringBuilder sb = new StringBuilder(); 

     for (String str : receivedMsg) { 
      sb.append(str); 
     } 
     System.out.println("Received: "+receivedMsg.toString()); 
     if (sb.toString().compareTo(MSG) == 0){ 
      System.out.printf("%s bytes sent and received!",sb.length()); 
     } 
     else{ 
      System.out.println("MESSAGE IS NOT EQUAL!"); 
     } 
    } 

    /* Splits the message equally */ 
    private static ArrayList<String> splitMessage(String message, int chunks) { 
     /* */ 
     ArrayList<String> packages = new ArrayList<>(
       (message.length() + chunks) - 1/chunks); 

     for (int i = 0; i < message.length(); i += chunks){ 
      packages.add(message.substring(i, Math.min(message.length(), 
        i + chunks))); 
     } 
     return packages; 
    } 

    public static boolean isCorrect(String[] args) { 

     /* Make sure all arguments are present */ 
     if (args.length != 4 && args.length == 0) { 
      printUsage(); 
      return false; 
     } 
     else 
      try { 

       HOST_NAME = args[0]; 
       MY_PORT = Integer.parseInt(args[1]); 
       MY_BUFFER = Integer.parseInt(args[2]); 
       RATE = Integer.parseInt(args[3]); 

      /* Ensures RATE is not too high with a tested limit of 3000 */ 
       if (RATE > 3000) { 
        System.err.println("Rate value is too large!"); 
        return false; 
       } 

      /* Make sure the host is valid */ 
       if (!isValidHost(HOST_NAME)) { 
        System.err.println("Host address is not valid!" + 
          "\nRequires a valid IP address or just localhost"); 
        return false; 
       } 

      /* Make sure the port number is in the valid range */ 
       if (MY_PORT <= 0 || MY_PORT >= 65536) { 
        System.err.println("Port value must be in (0 -> 65535)!"); 
        return false; 
       } 

      /* Make sure the buffer is at least 2, not lower */ 
       if (MY_BUFFER < 2){ 
        System.err.println("Buffer must be higher or equal to 2!"); 
        return false; 
       } 

      /* Split the message if bigger than buffer to appropriate packages */ 
       if (MSG.length() > MY_BUFFER) { 
        packages = (int) Math.ceil((double) MSG.length()/MY_BUFFER); 
        chars = (MSG.length()/packages); 
        originalMsg = splitMessage(MSG, chars); 
       } 

      /* Else adds whole message to array list */ 
       else { 

        packages = (int) Math.ceil((double)MSG.length()/MY_BUFFER); 
        chars = (MSG.length()/packages); 
        originalMsg = splitMessage(MSG, chars); 
       } 
      } 
      catch (IndexOutOfBoundsException e) { 
       printUsage(); 
       System.exit(1); 
      } 
      catch (NumberFormatException n) { 
       System.err.println("Invalid arguments!"); 
       printUsage(); 
       System.exit(1); 
      } 

     /* Everything is valid */ 
     return true; 
    } 

    private static boolean isValidHost(String host) { 

     /* Check if the string is valid */ 
     if (host == null || host.length() < 7 || host.length() > 15){ 
      return false; 
     } 
     else 

     /* Host is valid "localhost" */ 
      if (host.equals("localhost")){ 
       return true; 
      } 

     /* Check the host string, should be in x.x.x.x format */ 
     StringTokenizer token = new StringTokenizer(host,"."); 
     if (token.countTokens() != 4) 
      return false; 

     while (token.hasMoreTokens()) { 

      /* Get current token and convert to an integer value */ 
      String ip = token.nextToken(); 
      try { 

       int ipVal = Integer.valueOf(ip).intValue(); 
       if (ipVal < 0 || ipVal > 255) 
        return false; 
      } 
      catch (NumberFormatException ex) { 
       return false; 
      } 
     } 

     /* IP Address looks valid */ 
     return true; 
    } 

    private static void printUsage() { 
     System.err.println("Input arguments did not match expected arguments!" + 
       "\nUsage: \"<host_name> <port> <message_buffer> <message_rate>\""); 
    } 
} 

UDP Сервер:

/* 
    UDPEchoServer.java 
    A simple echo server with no error handling 
*/ 
import java.io.IOException; 
import java.net.*; 

public class UDPEchoServer { 
    public static final int BUFSIZE = 1024; 
    public static final int MYPORT = 4950; 
    public static boolean running = true; 

    public static void main(String[] args) { 
     byte[] buf = new byte[BUFSIZE]; 

     try{ 

      /* Create socket */ 
      DatagramSocket socket = new DatagramSocket(null); 

      /* Create local bind point */ 
      SocketAddress localBindPoint = new InetSocketAddress(MYPORT); 
      socket.bind(localBindPoint); 

      System.out.println("---------------------------------"+ 
        "\n| UDP Echo Server"+ 
        "\n| Configuration: "+ 
        "\n| port: "+MYPORT+ 
        "\n---------------------------------"); 

      while (running) { 

       /* Create datagram packet for receiving message */ 
       DatagramPacket receivePacket = new DatagramPacket(buf, buf.length); 

       /* Receiving message */ 
       socket.receive(receivePacket); 

       /* Create datagram packet for sending message */ 
       DatagramPacket sendPacket = 
         new DatagramPacket(receivePacket.getData(), 
           receivePacket.getLength(), 
           receivePacket.getAddress(), 
           receivePacket.getPort()); 

       String echo = new String(receivePacket.getData(), 
         receivePacket.getOffset(), receivePacket.getLength()); 

       System.out.printf("UDP echo request from %s", receivePacket.getAddress().getHostAddress()); 
       System.out.printf(" using port %d\n", receivePacket.getPort()); 
       System.out.println("Received: "+echo); 

       /* Send message*/ 
       socket.send(sendPacket); 
      } 
     } 
     catch (SocketException s){ 
      System.err.println(s.getMessage()); 
     } 
     catch (IOException e){ 
      System.err.println(e.getMessage()); 
     } 
    } 
} 
+0

Что вы ожидаете от своего вывода? На беглом взгляде кажется, что он работает хорошо. – RealSkeptic

+0

Да, он работает очень хорошо, когда у вас есть ставка 1. Я написал неправильную конфигурационную строку. обновил сообщение: – jabbeboy

ответ

1

Давайте посмотрим, что происходит, когда ваша ставка 5:

Это тело таймер:

    if (RATE == 0 || RATE == 1) { 
         for (int i = 0; i < RATE; i++) { 
          socket.send(sendPacket); 
          timer.cancel(); 
         } 
        } else { 
         for (int i = 0; i < RATE; i++) { 
          socket.send(sendPacket); 
          timer.cancel(); 
         } 

        } 

Итак, if условие false, потому что скорость не является ни 0, ни 1 Идём к else:

     for (int i = 0; i < RATE; i++) { 
          socket.send(sendPacket); 
          timer.cancel(); 
         } 

Для RATE = 5, это как письмо:

      socket.send(sendPacket); 
          timer.cancel(); 
          socket.send(sendPacket); 
          timer.cancel(); 
          socket.send(sendPacket); 
          timer.cancel(); 
          socket.send(sendPacket); 
          timer.cancel(); 
          socket.send(sendPacket); 
          timer.cancel(); 

Конечно, отменяя таймер пять раз не имеет никакого эффекта, но это - отправка одного и того же пакета 5 раз один за другим. Затем он отправит следующую часть 5 раз, а третья часть - 5 раз, потому что вы создаете три отдельных таймера для частей.

Я думаю, что если вы хотите отправить со скоростью 5 дейтаграмм в секунду (это смысл rate?), Вы не должны создавать столько таймеров, сколько есть частей. Вы должны создать один таймер, дать ему список датаграмм для отправки и установить его период графика 1000L/rate (убедитесь, что скорость не равна нулю!). Таймер должен вывести следующую дейтаграмму из списка и отправить ее. Если в списке нет дейтаграммы, она должна отменить себя.

  • один цикл, чтобы заполнить список с дейтаграмм
  • Присвоить список для окончательной переменной, которая может быть использована с анонимного класса или к полю.
  • Создайте таймер и запустите его с помощью 1000L/rate времени расписания.
  • Запустите второй цикл для чтения и сравнения полученных эхо-дейтаграмм.

Обратите внимание на две отдельные петли!

О повторной сборки дейтаграмм

Во-первых, обратите внимание, что DatagramPacket что вы получаете от сервера не то же самое, что DatagramPacket вы послали к нему даже если содержимое тот же! Это два разных объекта, а метод equals() от Object не переопределяется, что означает, что для любых двух объектов a и b типа DatagramPacket, a.equals(b) эквивалентно a == b.

Это означает, что единственное, что вы можете сравнить это содержание дейтаграммы , не дейтаграмма объект.

Поскольку UDP не гарантирует, что пакеты будут отправлены в любом конкретном порядке, вы должны сами позаботиться об этом. Это обычно означает, что вам нужно включить больше информации в полезную нагрузку датаграммы, чем просто содержимое строки. Хорошим местом для начала является добавление байта, который представляет номер части.

Например, предположим, что вы хотите отправить сообщение «ABCDEF» в двух пакетах, содержащих «ABC» и «DEF». То, что вы делаете сейчас посылает что-то вроде:

┌──┬──┬──┐ 
│65│66│67│ 
└──┴──┴──┘ 
┌──┬──┬──┐ 
│68│69│70│ 
└──┴──┴──┘

Теперь вы можете получить, что еще в

┌──┬──┬──┐ 
│68│69│70│ 
└──┴──┴──┘ 
┌──┬──┬──┐ 
│65│66│67│ 
└──┴──┴──┘

И вы не имеете никакого способа знать, что и вы будете повторно собрать его и его будет DEFABC.

Но если вы послали другие байты, что дает приказ:

┌─┬──┬──┬──┐ 
│0│65│66│67│ 
└─┴──┴──┴──┘ 
┌─┬──┬──┬──┐ 
│1│68│69│70│ 
└─┴──┴──┴──┘

вы получите первые байты, преобразовать его в целое, и преобразовать остальные байты в строку. Затем вы можете поместить его в список с помощью индекса, и они выйдут в правильном порядке, независимо от того, получили ли вы первый или второй пакет 1.

В реальной ситуации вы также отправите размер (количество пакетов) и идентификационный номер (так что, если вы получите некоторые израненные датаграммы, принадлежащие к старой связи, которая только что прибыла, они не будут Получите смешение с вашей собранной полезной нагрузкой.

+0

Hmm okey. Итак, если я понимаю 1. Перечислите список массивов с частями сообщения и добавьте части в пакет датаграммы, а затем в том же цикле добавьте все пакеты дейтаграмм в список массивов дейтаграмм? ? Тогда я не полностью понимаю ANDS. Что вы имеете в виду с 1000L/rate? Думаю, не нужен цикл for для курса? Для сравнения, вы подразумеваете в основном эту основную идею? 'for (пакет DatagramPacket: датаграммы)' socket.send (пакет); сравнить дейтаграмму каждый? send => compare => send => compare – jabbeboy

+0

1. Создайте список дейтаграмм. Loop на 'originalMsg', создавая датаграмму из каждой строки и помещая ее в список датаграмм. Затем создайте таймер, чья задача выдает датаграмму из этого списка и отправляет его. Он отменяет себя, если в списке больше нет дейтаграмм.Плановый таймер в основном повторяется до тех пор, пока он не отменяется, поэтому нет необходимости в цикле внутри таймера. Просто поместите одну дейтаграмму и отправьте ее. Вне таймера вам нужен второй цикл, который вызывает ваш метод readEchoPacket. – RealSkeptic

+0

Ahaa. Okej я сделал это до сих пор. Итак, в методе run() задачи таймера я буду использовать только для '(DatagramPacket datagram: datagramList) { socket.send (датаграмма);' Как вы хотите, чтобы я прочитал их? Могу ли я сохранить свою сборку дейтаграмм в построителе строк? Первая часть с отправкой их я думаю, что я понимаю, но не так, как я должен их читать. Также следует использовать 'timer.schedulerAtFixedRate (task, 0, 1000)'? – jabbeboy

Смежные вопросы