Mehrere SSH-Befehle gleichzeitig an verschiedene Empfänger senden

  • Antworten:9
  • OffenNicht stickyNicht beantwortet
  • Forum-Beiträge: 45

27.04.2017, 14:35:42 via Website

Hi,

ich schicke SSH-Befehle an Raspberry Pis. Sobald ich ein Signal schicke, startet eine Software im Terminal. Wenn ich aber einen weiteren Befehl schicke, solange die Software auf dem einen Pi noch läuft, wird das neue Signal erst verschickt, wenn das Programm beendet ist.

Ich habe dafür zwei Funktionen:

// Führt den angegebenen Befehl auf dem Raspberry Pi aus
public static ArrayList<String> sshBefehl(String befehl, String nutzername, String passwort, String ip, int port) throws Exception {

    Log.e("führe aus", nutzername + " - " + passwort + " - " + ip + " - " + port + " - " + befehl);

    // Erstelle das benötigte Objekt, um eine SSH-Verbindung aufbauen zu können.
    JSch jsch = new JSch();

    // Bereite ein paar Variablen vor, um Ausgaben der Konsole auslesen zu können.
    byte[] buffer = new byte[1024];
    ArrayList<String> lines = new ArrayList<>();

    // Füttere das Objekt mit allen nötigen Informationen, um eine Verbindung aufbauen zu können.
    Session session = jsch.getSession(nutzername, ip, port);
    session.setPassword(passwort);

    // Umgehe das Abgleichen nach dem richtigen Key. (Es wird nun eine Man-In-Middle Attacke nicht mehr abgefangen)
    Properties prop = new Properties();
    prop.put("StrictHostKeyChecking", "no");
    session.setConfig(prop);

    // Stelle eine Verbindung her.
    session.connect();

    // Erstelle ein neues Objekt, für einen neuen SSH Channel.
    ChannelExec channelssh = (ChannelExec) session.openChannel("exec");

    // Füttere den Channel mit dem Befehl und schicke den Befehl ab.
    channelssh.setCommand(befehl);
    channelssh.connect();

    // Fange an die Ausgaben der Konsole auszulesen.
    try {
        InputStream in = channelssh.getInputStream();
        String line = "";
        // Lese alle Ausgaben aus, bis der Befehl beendet wurde oder die Verbindung abbricht.
        while (true) {
            // Schreibe jede Zeile der Konsolenausgabe in unser Array.
            while (in.available() > 0) {
                int i = in.read(buffer, 0, 1024);
                // Brich die Protokollierung der Ausgabe, für diese Zeile, ab, wenn die Ausgabe leer sein sollte.
                if (i < 0) {
                    break;
                }
                line = new String(buffer, 0, i);
                lines.add(line);
            }
            // Wir wurden ausgeloggt.
            if (line.contains("logout")) {
                break;
            }
            // Befehl beendet oder Verbindung abgebrochen.
            if (channelssh.isClosed()) {
                break;
            }
            // Warte einen kleinen Augenblick mit der nächsten Zeile.
            try {
                Thread.sleep(1000);
            } catch (Exception ee) {}
        }
    }catch (Exception e){}

    // Beende alle Verbindungen.
    channelssh.disconnect();
    session.disconnect();

    // Gib die Ausgabe zurück
    return lines;
}

public static void executeBefehl(final String befehl, final String nutzername, final String passwort, final String ip, final int port){

    //Log.e("starte", nutzername + " - " + passwort  + " - " + ip  + " - " + port + " - " + befehl);

    // Hintergrundaufgabe erstellen
    new AsyncTask<Integer, Void, Void>() {
        @Override
        protected Void doInBackground(Integer... params) {
            try {

                // Funktion ausführen und Konsolenausgabe in "lines" speichern.
                ArrayList<String> lines = sshBefehl(befehl, nutzername, passwort, ip, port);
                // Alle Zeilen der Konsolenausgabe in den Android Logs ausgeben.
                while(!lines.isEmpty()){
                    Log.e("Rückgabe", lines.get(0));
                    lines.remove(0);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }
    }.execute(1);
}

Gibt es eine Möglichkeit mehrere Befehle auf einmal zu verschicken?

Danke im Voraus.

Antworten
  • Forum-Beiträge: 2.902

27.04.2017, 14:46:47 via Website

Hallo Basti,

Standard AsyncTask wartet solange, bis der vorherige beendet ist, deshalb hast du dein bloc.

Du musst dir dazu eine eigene Handler-Mimik bauen und deine Tasks mit THREAD_POOL_EXECUTOR ausführen
oder einen pure Thread anstatt AsyncTask verwenden.

Schau mal - ich bin selbst vor einigen Monaten dabei an die Wand gelaufen :
Lösung steht drunter !!!!!
https://www.androidpit.de/forum/720637/verstaendniss-problem-asynctask-s

— geändert am 27.04.2017, 14:50:39

Liebe Grüße - Stefan
[ App - Entwicklung ]

Antworten
  • Forum-Beiträge: 45

27.04.2017, 14:54:24 via Website

bedeutet das, dass ich das .execute(1) durch .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); ersetzen muss?

— geändert am 27.04.2017, 15:01:13

Antworten
  • Forum-Beiträge: 2.902

27.04.2017, 15:17:29 via Website

Nur so, wie im letzten Artikel von mir erwähnt.

Geht aber nicht mit deinem Code , den musst du umändern , dein AsyncTask muss eine eigene abgekapselte
Klasse darstellen , damit du es auch so aufrufen kannst.

P.S nicht den Mimik-Handler vergessen!! - wie beschrieben ( canceling)

Bsp

private class _async_mytask extends AsyncTask < Void, Void, Void >
{
......
}

Aufruf :

new _async_mytask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);

— geändert am 27.04.2017, 15:26:03

Liebe Grüße - Stefan
[ App - Entwicklung ]

Antworten
  • Forum-Beiträge: 45

27.04.2017, 15:30:03 via Website

also ich hab das ganze jetzt mal probiert, indem ich wie oben beschrieben das execute(1) ersetzt habe. und es läuft genauso wie ich das wollte. passt also alles!

Danke! echt klasse!

Antworten
  • Forum-Beiträge: 2.902

27.04.2017, 15:42:41 via Website

Das Freut mich ..

Allerdings einen lieb gemeinten Hinweis :

Du solltest Dir schon die Arbeit machen, es so umzustellen , wie ich dir eben gezeigt habe.
So wie du es anwendest läuft es zwar , hast es aber eher non OOP-conform.

Irgendwann verlierst du den Überblick und kannst auch deine Tasks nicht vernünftig abbrechen.

Du baust dir da eben ein SpaghettiProgramm zusammen, welches immer länger wird :-)

Liebe Grüße - Stefan
[ App - Entwicklung ]

Antworten
  • Forum-Beiträge: 45

27.04.2017, 15:44:51 via Website

Da hast du recht ja. Werde ich mir zu Herzen nehmen. Und danke nochmal!

Antworten
  • Forum-Beiträge: 45

27.04.2017, 15:52:25 via Website

schreibe ich in meine Funktion

private class _async_mytask extends AsyncTask < Void, Void, Void >
{
......
}

dann meine zwei Funktionen.. steh grad ein bisschen auf dem Schlauch.

Antworten
  • Forum-Beiträge: 2.902

27.04.2017, 16:30:07 via Website

Ich habe grundsätzlich gemeint, das du alles ein wenig Auslagern sollst.

Also nehmen wir die executeBefehl...

die lässt du so stehen , aber deinen AsynTask baust du bitte wiederum in eine eigene Klasse - am besten in ein eigenes Java File und innerhalb executeBefehl rufst du dann new _asyn_mytask ... auf...

Parameter kannst du ja im Constructor übergeben.

Somit bekommst du auch Ordnung rein

Liebe Grüße - Stefan
[ App - Entwicklung ]

Antworten

Empfohlene Artikel