[java] Wywoływanie w pętli polecenia PING i innych

0

Witam

Eksperymentuje trochę z wywoływaniem poleceń windowsowych spod Javy i mam taki problem z poleceniem ping.

Wywołuje te polecenie w następujący sposób:
Metoda:

    public void Pingt (String AdresTmp) throws IOException{
        String TMP = "";
        Process ping = r.exec("ping -n 1 "+AdresTmp);
        String PingResult = "";
        try (BufferedReader in = new BufferedReader (new InputStreamReader (ping.getInputStream()))){
            String inputPing;
            while ((inputPing = in.readLine()) != null){
                TMP += inputPing+"\n";
                if (inputPing.contains("Minimum")){
                    char czas10, czas11, czas12;
                    String tmp;
                    czas10 = inputPing.charAt(13);
                    czas11 = inputPing.charAt(14);
                    czas12 = inputPing.charAt(15);
                    tmp = Character.toString(czas10)+Character.toString(czas11)+Character.toString(czas12);
                    wynik.insert("Czas Odpowiedzi: "+tmp+" ms\n", 0);
                }
                if (inputPing.contains("Host docelowy jest")){
                    wynik.insert("Host docelowy jest nieosągalny, \nwpisz ponownie poprawny adres IP \n", 0);
                }
            }
        }
    } 

Oraz wywołanie za pomocą Buttona:

        String AdresTmp;
        AdresTmp=adres.getText();
        String IloscZapytan;
        IloscZapytan = n.getText();
        int IntIloscZapytan = 0;
        try {
            IntIloscZapytan = Integer.parseInt(IloscZapytan);
        } catch (Exception ex) {
            wynik.insert("Błąd - wprowadź ilość zapytań większą od zera\n", 0);
        }
        for (int i = IntIloscZapytan; i>0; i--){
                try {
            Pingt(AdresTmp);
        } catch (IOException ex) {
            Logger.getLogger(oknoglowne.class.getName()).log(Level.SEVERE, null, ex);
            wynik.insert("Podano niepoprawny adres IP", 1);
        } catch (Exception Ex){
            wynik.insert("Nieoczekiwany Błąd", 0);
        }      
        }    

Obecnie program działa tak, że podaje adres IP do pingowania oraz liczbę obiegów polecenia ping. Metoda Ping jest wywoływana w pętli for, więc wydawało mi się, że program powinien wykonać pinga określonego hosta, wypisać czas odpowiedzi, a następnie wykonać polecenie ponownie, tak długo, aż i nie będzie 0;
Jednak jeżeli wpiszę adres IP i wpiszę 100 powtórzeń, to program zamiast wyświetlać opóźnienie po każdym wykonaniu pętli for, to przestaje odpowiadać na jakieś 15 sekund, a następnie wyświetla wszystkie 100 czasów na raz. Chciałbym, żeby działało to jak polecenie ping -t w dosowskiej konsoli, ponieważ w efekcie chciałbym uzyskać program, który będzie pingował jakiegoś hosta w określonej pętli, a następnie z czasów opóźnienia tworzył mi wykres, w czasie rzeczywistym lub w na żądanie. Co prawda do tworzenia wykresu jeszcze nie doszedłem, bo przy obecnym wykonywaniu programu jak dam mu jakiś adres i na przykład i 5000 zapytań to sobie mogę iść na obiad z deserem czekając na wyniki. Chciałbym, żeby wykonywało się to w czasie rzeczywistym.

Jeszcze pytanie poza konkursem. Jak zrobić, żeby za pomocą jakiegoś buttona przerwać wykonywanie polecenia rountime? Na przykład wywołuje Runtime ping -t www.onet.pl w tym moim okienku pojawiają mi się na bieżąco czasy opóźnienia, a jak mi się znudzi to jakimś buttonem chciałbym zkillować proces ping -t.

1

No wiec, akcja wykonywana w actionListenerze jest wykonywana cala w obrebie watku EDT (event dispatch thread) tworzonym przez swinga. Updaty GUI sa rowniez robione w tym watku. Jak pewnie powoli sam zauwazasz, updaty GUI (odmalowanie, zmiana statusu guzika, wyswietlenie nowego napisu na jakiejs text area, ...) nie moga zostac wykonane dopoki jest wykonywany jakis inny kod. W Twoim przypadku: dluga akcja w actionListenerze zajmuje caly watek EDT i nie pozwala zeby sie cos odrysowalo. Rozwiazanie jest proste / trudne ;d musisz uzyc wiecej watkow. Trudne poniewaz musisz sie bawic watkami - a to nie jest trywialne, chociazby nie wiem ile osob na tym forum zaraz podnioslo larum ze jest inaczej; latwoe, poniewaz swing ma gotowe klasy do tego - SwingWorker (mam nadzieje ze uzywasz jdk >= 1.6). W skrocie - startuje nowy watek, ktory jest reprezentowany przez klase rozszerzajaca SwingWorkera, ale masz mozliwosc zaimplementowania roznych metod ktore sa wykonywane najczesciej w EDT - np. publish czy update (nie pamietam dokladnie nazw). W tych metodach zmieniasz GUI (np. pokazujesz status pinga w text area) a samo pingowanie leci sobie dalej i jest cacy.

1

Pytanie pozakonkursowe - SwingWorker ma metode sluzaca do przerwania operacji, zatem musisz zrobic tak, zeby drugi guzik dostal jakos referencje do workera i wywolal ta metode.

0

Trochę poczytałem i na chwilę obecną wygląda to tak:
Deklaracja klasy i jej metod:

    public class Pingt extends javax.swing.SwingWorker {
        //String Adres1;
        public void Pingt1 (String AdresTmp) throws IOException{
        String TMP = "";
        Process ping = r.exec("ping -n 1 "+AdresTmp);
        String PingResult = "";
        try (BufferedReader in = new BufferedReader (new InputStreamReader (ping.getInputStream()))){
            String inputPing;
            while ((inputPing = in.readLine()) != null){
                TMP += inputPing+"\n";
                if (inputPing.contains("Minimum")){
                    char czas10, czas11, czas12;
                    String tmp;
                    czas10 = inputPing.charAt(13);
                    czas11 = inputPing.charAt(14);
                    czas12 = inputPing.charAt(15);
                    tmp = Character.toString(czas10)+Character.toString(czas11)+Character.toString(czas12);
                    wynik.insert("Czas Odpowiedzi: "+tmp+" ms\n", 0);
                }
                if (inputPing.contains("Host docelowy jest")){
                    wynik.insert("Host docelowy jest nieosągalny, \nwpisz ponownie poprawny adres IP \n", 0);
                }
            }
        }
    }

        @Override
        protected Object doInBackground() throws Exception {
            throw new UnsupportedOperationException("Not supported yet.");
        }
    }

Oraz kod przypisany do buttona:

    private void badajActionPerformed(java.awt.event.ActionEvent evt) {                                      
        String IloscZapytan;
        IloscZapytan = n.getText();
        int IntIloscZapytan = 0;
        try {
            IntIloscZapytan = Integer.parseInt(IloscZapytan);
        } catch (Exception ex) {
            wynik.insert("Błąd - wprowadź ilość zapytań większą od zera\n", 0);
        }
        SwingWorker worker = new SwingWorker<Pingt, Void>() {
            @Override
            public Pingt doInBackground() throws Exception {
                Pingt ping1 = new Pingt();
                String Adres1= adres.getText();
                ping1.Pingt1(Adres1);
               throw new UnsupportedOperationException("Not supported yet.");
            }
            };

Problem obecnie polega na tym, że niby błędów nie ma żadnych, jak dla mnie kod wydaje się logiczny, jednak program nie działa. Po prostu w TexField wynik nie wyświetla mi nic. W czym może polegać problem?

1

Twoj Pingt jest SwingWorkerem, a w actionPerformed tworzysz innego ktory wewnetrznie tworzy Pingt - tak ma byc? Poza tym, tego workera musisz gdzies wystartowac - metoda execute().
Ogolnie powinno to wygladac tak, ze tworzysz SwingWorkera ktory:

  • w metodzie doInBackground w petli pinguje i po kazdej odpowiedzi wywoluje metode publish() z jakims parametrem (String? cokolwiek, jest generyczne)
  • w metodzie process() dostajesz liste argumentow ktora zawiera potencjalnie wiele wartosci z ktorymi wywolales metode publish() - wynika ot z tego ze moze sie zdarzyc ze metoda publish zostanie wywolana wiele razy z w miedzyczasie process tylko raz i moze chcesz przerobic wszystkie tymczasowe wyniki, a moze tylko ostatni?; w tejze metodzie process dodajesz cos do pola tekstowego, text area czy cokolwiek
  • wywolujesz na tym SW metode execute()

Jeszcze jedno - ogolnie powinienes to zrobic tak, zeby Twoj Pingt nie mial zadnych zaleznosci od Swinga, a twoja podklasa SwingWorker tylko z niego korzystala. Zdaje sobie sprawe ze jak jestes mniej doswiadczony to moze to byc problemem, ale moze Ci sie uda.
I jeszcze jedno - dokumentacja swingworkera z tego co pamietam ma pare przykladow jak tego uzywac, na jej podstawie powinienes dac rade zrobic co chcesz.

0

Czyli powinno to wyglądać mniej więcej tak. Chce tylko wiedzieć, że dobrze zrozumiałem przesłanie:

public class Pingt {
			        @Override
       				 protected Object doInBackground() throws Exception {
				
				Pętla for wykonująca polecenie ping i zapisująca wynik pingowania do jakiegoś stringa WynikPingowania 
				
				protected final void publish (wynik_pingowania);
				
           			 throw new UnsupportedOperationException("Not supported yet.");
       					 }
    					}
				
				

Nie widzę szczerze powiedziawszy potrzeby stosowania dwóch pozostałych metod. Przynajmniej na moim obecnym poziomie wiedzy. Ponieważ metoda execute jest wymagana, gdy używam swing workera więcej niż raz. Jednak ja swingworkera będę wywoływał raz , a dalej już polecenie będzie działało w pętli. A po co jest metoda process, to niestety nie zrozumiałem z tej: http://docs.oracle.com/javase/6/docs/api/javax/swing/SwingWorker.html dokumentacji. W necie jakiś helpów o tej klasie nie jest zbyt wiele.
Oczywiście rozumiem, że metoda Publish zwróci mi Stringa z wynikiem mojego programu, ale potem żeby go wyświetlić, to musiałbym chyba jakoś w pętli wywoływać wprowadzanie tego na konsole. Chyba, że można jakiegoś Listenera podpiąć pod tą metodę. Jakoś dużo niejasności jest jak dla mnie przy tej klasie.
Druga kwestia to problem z wywołaniem tej funkcji buttonem. Jedynie co to wiem, że ten button musiałby tworzyć nowy obiekt klasy Pingt. Następnie musiałbym wywołać metodę doInBackground z klasy Pingt. Chyba, że można olać tworzenie klasy Pingt i wrzucić swingworkera jako metodę a nie jako klasę i button miałby wywoływać wtedy konkretną metodę. Chciałbym znać pełną metodologię zanim zacznę pisać czysty kod, ponieważ później jakoś mnie strasznie nerwy łapią jak coś mi nie działa, a ja nie wiem dlaczego.

1

To powinno wygladac mniej wiecej tak:

  • guzik start tworzy workera i go startuje (nie rozumiem dlaczego uwazasz ze nie musisz go uruchomic), ten z kolei puszcza pingi i po otrzymaniu wyniku kazdego znich wola metode publish z wynikiem
  • metoda process przerabia wynika dodajac do text area
  • metoda done robi cos ne koncu - ja akurat zaznaczam ladnie gdzie pingi zostaly przerwane
  • guzik stop przerywa pingi, tak jak chciales - wola metode cancel()

Uruchom pingi kilka razy, i raz przerwij a raz poczekaj do konca (10 pingow), zobaczysz ze ladnie i spojnie dziala.

Jeszcze jedno - ten kod jest bardzo brzydki, szczegolnie to statyczne pole do ktorego zapisywany jest worker - ale mam nadzieje ze sobie juz poradzisz z uszlachetnieniem kodu ;d

import java.awt.BorderLayout;
import java.awt.Button;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.List;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingWorker;
import javax.swing.WindowConstants;


public class Main {

    private static SwingWorker<Void, String> worker;

    public static void main(String[] args) {
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

        final JTextArea pingArea = new JTextArea(20, 20);
        pingArea.setEditable(false);
        JScrollPane pingScroll = new JScrollPane(pingArea);
        frame.add(pingScroll, BorderLayout.CENTER);

        JPanel actionPanel = new JPanel();
        final JButton pingButton = new JButton("Ping!");
        final JButton stopButton = new JButton("Stop");
        stopButton.setEnabled(false);
        actionPanel.add(pingButton);
        actionPanel.add(stopButton);
        frame.add(actionPanel, BorderLayout.NORTH);

        pingButton.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                pingButton.setEnabled(false);
                worker = new SwingWorker<Void, String>() {

                    @Override
                    protected Void doInBackground() {
                        for (int i = 1; i <= 10; ++i) {
                            String result = "ping " + i; // real ping logic here
                            publish(result);
                            try {
                                Thread.sleep(500);
                            } catch (InterruptedException e) {
                                // if worker started, and cancel called, thread interrupt is sent
                                // so obey here and break out of the loop
                                break;
                            }
                        }
                        return null;
                    }

                    @Override
                    protected void process(List<String> chunks) {
                        for (String ping : chunks) {
                            // process every ping result - append to area
                            pingArea.append(ping + "\n");
                        }
                    }

                    @Override
                    protected void done() {
                        // when pinging done (all finished or cancelled) do something special
                        // this is just to present the concept
                        pingArea.append("ping done!\n\n");
                        pingButton.setEnabled(true);
                        stopButton.setEnabled(false);
                    }
                };
                worker.execute();
                stopButton.setEnabled(true);
            }
        });

        stopButton.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                worker.cancel(true);
            }
        });

        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }
}
0

Dzięki wielkie. Trochę to przerobiłem na swoje potrzeby, działa jak należy. Nie chciałem w zasadzie wyłudzać od nikogo właściwie gotowy kod programu, chciałem tylko przepis na babkę, ale w zasadzie gotowy przykład najbardziej pomógł mi zrozumieć zasadę działania. Jeszcze raz dzięki.

1 użytkowników online, w tym zalogowanych: 0, gości: 1