2017-02-14 15 views
0

Я пишу приложение IP-сканер и процесс занимает много времени, так что я использовал на задней сцене графического интерфейса является услуга исполнителя, как:Как запустить сервис-исполнителя от Swing worker?

public static List<Future<String>> checkThisIP(String ipStart, String ipEnd) throws UnknownHostException { 
    final ExecutorService es = Executors.newFixedThreadPool(10); 
    final List<Future<String>> futures = new ArrayList<>(); 
    String ipStringStart; 
    String ipStringEnd; 
    String targetIpString; 
    //my update 
    ipStringStart = ipStart.substring(ipStart.lastIndexOf(".") + 1, ipStart.length()); 
    ipStringEnd = ipEnd.substring(ipEnd.lastIndexOf(".") + 1, ipEnd.length()); 
    targetIpString = ipStart.substring(0, ipStart.lastIndexOf(".") + 1); 
    if (!ipStart.equals(ipEnd)) { 
     for (int i = Integer.parseInt(ipStringStart); i <= Integer.parseInt(ipStringEnd); i++) { 
      String currentIp = targetIpString + i; 
      futures.add(runPingScan(es, currentIp)); 
     } 
    } else { 
     futures.add(runPingScan(es, ipStart)); 
    } 
    es.shutdown(); 
    return futures; 
} 


public static Future<String> runPingScan(final ExecutorService es, final String ip) { 
    return es.submit(new Callable<String>() { 
     @Override 
     public String call() { 
      String returnMe = ""; 
      //custom ping class 
      Ping p = new Ping(); 
      //send message 
      p.SendReply(ip); 
      //IsReachable returns ture or false 
      if(p.IsReachable()){ 
       returnMe=ip; 
      } 
      return returnMe; 
      } 

    }); 
} 

Это оригинальный лага кода действие предварительно с помощью JButton :

// scan result is Future list returned from service executor 
    List<Future<String>> scanResult = p.checkThisIP(jFormattedTextField1.getText(), jFormattedTextField2.getText()); 
        for (final Future<String> f : scanResult) { 
         try { 
          ip = f.get(); 
          if (!ip.equals("")) { 
           arp ARP = new arp(); 
           PortScan openPort = new PortScan(); 
           IP ipClass = new IP(); 
           mac = ARP.getMac(ip); 
           manufacturer = ARP.getOUI(mac); 
           ports = openPort.checkIpForPorts(ip); 
           hostname = ipClass.hostname(ip); 
           title = ipClass.htmlTitle(ip); 
           Object[] data = {ip, mac, manufacturer, ports, hostname, title}; 
           tableModel.addRow(data); 
          } 

          if (jFormattedTextField1.getText().equals(jFormattedTextField2.getText()) && ip.equals("")) { 
           JOptionPane.showMessageDialog(null, "<html>Can not ping the address ! <br> Server might be protected by <b>WAF</b>.</html>", "Alert", HEIGHT); 
          } 
         } catch (Exception ex) { 
          Logger.getLogger(gui.class.getName()).log(Level.SEVERE, null, ex); 
         } 
        } 

Выполнение этого кода это хорошо, но когда я прикрепить его к кнопку Начать проверку ГПИ-лаги, я гугл и понял, использовать Swing Worker. Когда я реализовал только качели, он убил параллелизм, и когда я реализовал оба gui все еще лага. Мой вопрос заключается в том, чтобы заставить кнопку (рабочий Swing) вызвать исполнителя службы для выполнения других процессов?

ответ

1

Мне удалось решить мою проблему, выполнив работу качели, а функция сделать на заднем плане начнет новую тему для исполнителя сервиса и предотвратит отставание.

//The actionpreformed by the button 
    SwingWorker worker = new SwingWorker<Void, Void>() { 
      @Override 
     // All actions are done this method 
      protected Void doInBackground() throws Exception { 
       String ip = ""; 
       String mac = ""; 
       String manufacturer = ""; 
       String ports = ""; 
       String hostname = ""; 
       String title = ""; 
       tableModel.setRowCount(0); 
       PingScan p = new PingScan(); 
       List<Future<String>> scanResult = p.checkThisIP(jFormattedTextField1.getText(), jFormattedTextField2.getText()); 
       for (final Future<String> f : scanResult) { 
        try { 
         ip = f.get(); 
         if (!ip.equals("")) { 
          arp ARP = new arp(); 
          PortScan openPort = new PortScan(); 
          IP ipClass = new IP(); 
          mac = ARP.getMac(ip); 
          manufacturer = ARP.getOUI(mac); 
          ports = openPort.checkIpForPorts(ip); 
          hostname = ipClass.hostname(ip); 
          title = ipClass.htmlTitle(ip); 
          Object[] data = {ip, mac, manufacturer, ports, hostname, title}; 
          tableModel.addRow(data); 
         } 
        } catch (Exception e) { 
         System.out.println(e.getMessage()); 
        } 
       } 
       return null; 
      } 
     }; 
    worker.execute(); 
1

Когда я реализовал работника качания, он убил параллелизм, и когда я реализовал оба gui все еще лага.

Там две вещи, чтобы сделать здесь:

  • распространяющиеся чеками пинг над несколькими потоками

    • разделить свою задачу на независимые подзадачи
    • пробег суб-задачи в ниток-бассейн
    • Результаты поиска
  • отсоединения всей операции от события Dispach нити

    • действия регистра пользователя (нажмите, нажатие), получить данные из текстовых полей, построить задачу
    • задачу запуска вне EDT
    • обновления графический интерфейс, показывающий результаты

Вы делаете первую часть с ExecutorService, для сома e вашего кода. Вторая часть не выполняется в вашем коде, поэтому EDT будет блокироваться до завершения всей операции, что приведет к вашей задержке.

Вам нужно перенести этот код на качелях работника, который выполняет задачи в исполнитель:

List<Future<String>> scanResult = p.checkThisIP(jFormattedTextField1.getText(), jFormattedTextField2.getText()); 
       for (final Future<String> f : scanResult) { 
        try { 
         [...] // this is where the thread blocks, making your ui lag if it's the EDT 
         Object[] data = {ip, mac, manufacturer, ports, hostname, title}; 

Во-первых, переместить все блокирующий код, который будет обрабатываться пул потоков на исполнителя:

public static Future<Object[]> runPingScan(final ExecutorService es, final String ip) { 
    return es.submit(new Callable<Object[]>() { 
     @Override 
     public Object[] call() { 
      //custom ping class 
      Ping p = new Ping(); 
      //send message 
      p.SendReply(ip); 
      //IsReachable returns ture or false 
      if(p.IsReachable()){ 
       [...] // other blocking code 
       return {ip, mac, manufacturer, ports, hostname, title}; 
      } else { 
       // special case, use null values or throw an exception 
      } 
     } 
    }); 
} 

Затем вы можете использовать код Simple Background Tasks учебник, чтобы отделить все это от EDT:

SwingWorker worker = new SwingWorker<List<Object[]>, Void>() { 
    public List<Object[]> doInBackground() { 
     // -- this will run in another thread -- 
     // submit ping checks to the executor 
     List<Future<Object[]>> scanResult = [...] 
     // get results, put them in a list, return it 
     List<Object[]> result = new ArrayList<>(); 
     for(Future<Object[]> f : scanResult) { 
      result.add(f.get()); // blocking happens here, outside of the EDT 
     } 
     return result; 
    } 

    public void done() { 
     // -- this will run in the EDT -- 
     // get() the list created above 
     // display the result in the gui 
     for(Object[] data : get()) { 
      tableModel.addRow(data); 
     } 
    } 
}; 

Что здесь не включено, это специальные случаи, такие как неудачная проверка ping, вам нужно как-то справиться с ними.Каждое исключение, выкинутое из ваших вызовов, возвращается при вызове f.get(), завернутое в ExecutionException. Использование этого для этих особых случаев, вероятно, является вашим лучшим вариантом.

+0

Спасибо за разъяснение +1. Обратите внимание, что первым параметром Swingworker должен быть List >, и я пробовал ваш ответ, но я столкнулся с таким же отставанием. Посмотрите на мой ответ, я использовал функцию swingworker ** doInBackground() ** для вызова функций, которые используют ** сервис-исполнитель ** (причина задержки) без какой-либо другой функции swingfunction. –

+0

Параметр первого типа определяет данные, созданные 'doInBackground', которые позже вызывается вызовом' get'. Он предназначен для того, чтобы просто быть списком строк, не нужно фьючерсов, так как все проверки ping будут завершены в этот момент. Если вы все делаете внутри 'doInBackground', убедитесь, что ваша модель правильно управляет обновлениями gui (т. Е.' InvokeLater'), поскольку Swing не является потокобезопасным, и все, что касается его компонентов, должно делать это из EDT. – duckstep

+0

Насколько я знаю, список строк не может принимать List >. 'Список <Будущее > scanResult = p.checkThisIP (ip1, ip2); // функция возвращает список Future ', поэтому, если мы хотим вернуть этот список из' doInbackground() ', первый параметр должен быть' > '. P.S: Я пробовал попробовать сам. Благодаря ! –