Verbindung zu FTP Server schlägt fehl

lordzwieback

lordzwieback

Erfahrenes Mitglied
210
Hallo,

ich habe folgendes Problem. Ich habe die Apache FTP Komponente eingerichtet und das funktioniert auch. Nun habe ich folgende Methode zum Verbinden des FTP Servers geschrieben:

Code:
// CONNECT TO FTP SERVER VIA PARAMETERS
    public boolean ftpConnect(String host, String username, String password) {
        try {
            mFTPClient = new FTPClient();
            System.out.println("1");
            //connection to host
            mFTPClient.connect(host, 21);
            System.out.println("2");
            // check the reply code
            if (FTPReply.isPositiveCompletion(mFTPClient.getReplyCode())) {
                // login using username and password
                boolean status = mFTPClient.login(username, password);

                mFTPClient.setFileType(FTP.BINARY_FILE_TYPE);
                mFTPClient.enterLocalPassiveMode();

                System.out.println("connected to host " + host);
                return status;
            }

        } catch (Exception e) {
            System.out.println("Error: could not connect to host " + host);
        }
        return false;
    }

Folgendes Problem:
Wie man sieht, konnte ich das Problem schon eingrenzen. Ich habe mir zwei Ausgaben erstellt ("1", "2"). Die 1 wird ausgegeben, danach springt die App in die Exception und gibt mir die Errormeldung aus. Die Frage ist nun, wieso er bei dem .connect() hängen bleibt.

Ich habe vorher auch getestet, den Port als Parameter anzugeben. Ebenso habe ich es ohne Port versucht. Ich habe gleichzeitig Filezilla offen gehabt und die Parameter, welche im Code stehen genau so bei Filezilla eingetragen. Da komme ich ohne Probleme auf den FTP Server.

Nach einigem hin und her googlen bin ich noch auf den Tipp gestoßen, man solle die Internet Permission in der Manifest Datei angeben. Das habe ich folgendermaßen gemacht:

Code:
<uses-permission android:name="android.permission.INTERNET"/>

Das hat aber auch nichts geholfen. Hoffe, jemand von euch hat noch eine Idee, die mir weiterhilft. Android Version = 6.0.1


Bis dahin viele Grüße, lordzwieback

EDIT: Die genaue Exception lautet "android.os.NetworkOnMainThreadException"
 
Hallo,

die Meldung sagt es eigentlich recht klar: Du rufst ftpConnect vom UI Thread aus auf. Das ist unter Android nicht erlaubt.
Suche mal nach Dokumentation zur Klasse AsyncTask, die zum Kapseln solcher Aktionen gedacht ist.
 
DieGoldeneMitte schrieb:
Hallo,

die Meldung sagt es eigentlich recht klar: Du rufst ftpConnect vom UI Thread aus auf. Das ist unter Android nicht erlaubt.
Suche mal nach Dokumentation zur Klasse AsyncTask, die zum Kapseln solcher Aktionen gedacht ist.
Das habe ich vor ca. 5 Minuten auch gefunden (also Stichwort UI Thread und Operation Threads, Async usw). Werde mir das mal anschauen und mich bei Bedarf dann nochmal melden. Danke für den Hinweis. :)
 
Nachtrag ...

AsynTask nur für kürzere Operationen benutzen ( ein paar sekunden) ansonsten muss ein
richtiger Thread her

AsyncTask is designed to be a helper class around Thread and Handler and does not constitute a generic threading framework. AsyncTasks should ideally be used for short operations (a few seconds at the most.) If you need to keep threads running for long periods of time, it is highly recommended you use the various APIs
 
@swa00 Geht hier erstmal um einen Test. Es soll eine Verbindung mit einem FTP Server zustande kommen.

Ich habe jetzt mal eine neue Klasse "FTPActivity" erstellt, Inhalt sieht momentan so aus:

Code:
public class FTPActivity extends AsyncTask<String,Void, Boolean> {
    @Override
    protected Boolean doInBackground(String... strings) {
            try {
                FTPClient mFTPClient = new FTPClient();
                System.out.println("1");
                //connection to host
                mFTPClient.connect(host, 21);
                System.out.println("2");
                // check the reply code. if positive -> connection is working
                if (FTPReply.isPositiveCompletion(mFTPClient.getReplyCode())) {
                    // login using username and password
                    boolean status = mFTPClient.login(username, password);

                    //define the correct transfer mode -> binary (for text, images, compressed files etc)
                    mFTPClient.setFileType(FTP.BINARY_FILE_TYPE);
                    mFTPClient.enterLocalPassiveMode();

                    //Toast.makeText(getApplicationContext(), "Connected to host " + host, Toast.LENGTH_SHORT).show();
                    System.out.println("conected to host " + host);
                    return status;
                }

            } catch (Exception e) {
                System.out.println("Error: could not connect to host " + host + " // " + e);
            }
            return false;
        }
    }

Ich stehe grade vor der Frage, wie ich die Parameter (host, username, password) übertragen bekomme. Aber ich gehe mal stark davon aus, dass ich da generell noch etwas falsch mache. Ich wühl mich mal weiter durch.
 
a) die Bezeichnung "FTPActivity" ist verwirrend , das ist keine Activity lieber sowas hier
_async_ftptask


b) Zu deiner Frage

Dafür bastelst du dir einen Constructor
private String host = "";
public _asyn_ftp_task (String myhost)
{
this.host = myhost;
}

P.S deine Parameter sind falsch, Kann es sein , dass du das Beipiel irgendwo herauskopiert hast ???
Lies mal das hier, damit du genau verstehst , worum es geht
Android background processing with Handlers, AsyncTask and Loaders - Tutorial
 
Zuletzt bearbeitet:
  • Danke
Reaktionen: lordzwieback
Nur nebenbei, in Android-Apps sollte man nicht System.out sondern die Klasse Log benutzten. Also z.B. Log.i(TAG, "irgendwas"); so kann man z.B. den logcat relativ einfach nach den Log-Leveln filtern, usw
Log | Android Developers
 
  • Danke
Reaktionen: lordzwieback
Danke für eure Tipps. Bin gerade erst wieder dazu gekommen, mir eure Beiträge und mein Projekt anzuschauen.

@swa00 Meinst du mit Parameter "AsyncTask<String,Void, Boolean>" ? Ich habe mich durch die Doku zum AsyncTask gewühlt und ich meine gelesen zu haben, dass der erste Parameter für die Daten steht, die der Task verarbeiten muss (hostname, username, password = String) und der letzte für den Rückgabewert (Boolean). Deshalb diese Parameter. Aber wenn du so fragst lag ich da wohl falsch. :D

@burgerohnealles Das habe ich auch schon gesehen und probiert, nur hat er mir da (sry, schon ein paar Wochen her) Log nur rot unterstrichen und ich hab nicht herausgefunden, wieso. Deshalb nun notgedrungen System.out.
 
Log nur rot unterstrichen und ich hab nicht herausgefunden, wieso. Deshalb nun notgedrungen System.out.

Nun , dann hast du nicht den Import mit ALT + ENTER gemacht :)
 
  • Danke
Reaktionen: lordzwieback
swa00 schrieb:
Nun , dann hast du nicht den Import mit ALT + ENTER gemacht :)
Werde das gleich mal testen, danke für die Hilfe und Geduld mit mir. Ich weiß, dass ich nicht immer der bin, der alles direkt versteht. ^^
[doublepost=1484126942,1484124327][/doublepost]@swa00 @burgerohnealles Ich glaube ich habe es hinbekommen. Was haltet ihr von meiner Lösung?

AsyncTask Subklasse:
Code:
// AsyncTask subclass
    private class _async_ftp_conn extends AsyncTask<String, Void, Boolean> {
        private String host = "";
        private String username = "";
        private String password = "";
      
        // constructor method
        public _async_ftp_conn(String hst, String usr, String pwd) {
            this.host = hst;
            this.username = usr;
            this.password = pwd;
        }

        @Override
        protected Boolean doInBackground(String... strings) {
            try {
                FTPClient mFTPClient = new FTPClient();
                //connection to host
                mFTPClient.connect(host, 21);
                // check the reply code. if positive -> connection is working
                if (FTPReply.isPositiveCompletion(mFTPClient.getReplyCode())) {
                    // login using username and password
                    boolean status = mFTPClient.login(username, password);

                    //define the correct transfer mode -> binary (for text, images, compressed files etc)
                    mFTPClient.setFileType(FTP.BINARY_FILE_TYPE);
                    mFTPClient.enterLocalPassiveMode();

                    //Toast.makeText(getApplicationContext(), "Connected to host " + host, Toast.LENGTH_SHORT).show();
                    //System.out.println("conected to host " + host);
                    Log.i(TAG, "connected to host " + host);
                    return status;
                }

            } catch (Exception e) {
                //System.out.println("Error: could not connect to host " + host + " // " + e);
                Log.e(TAG, "Error: could not connect to host " + host + " // " + e);
            }
            return false;
        }
    }

Aufruf des AsyncTasks beim Klick auf einen Testbutton:
Code:
// Testbutton FTP Connect
        ftpconn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                new _async_ftp_conn("***.**.***.***", "Hier Username eintragen", "Hier Passwort eintragen").execute();
            }
        });

Ausgabe Log (!) :D
I/MainActivity: connected to host ***.**.***.***

Scheint also zu funktionieren. Einige weiterführende Fragen:
Zu Log: Das ist jetzt im Prinzip das gleiche Ergebnis wie beim System.out, nur dass ich in meiner Klasse am Anfang eine Konstante deklariere und dadurch immer weiß, wo die Meldung ausgegeben wird?

Zum AsyncTask:
Wenn ich hier jetzt noch weitere Tasks (z.B. Disconnect, Ordner/Datei herunterladen usw) einfügen möchte, kann ich dann die aktuelle Subklasse irgendwie erweitern mit zusätzlichen Funktionen oder muss ich für jede Interaktion mit dem FTP Server einen eigenen Async Task anlegen?

Viele Grüße und Danke.

EDIT: Aus irgendeinem Grund ist es egal, welche Login-Daten ich eingebe. Hauptsache die Serveradresse stimmt. Korrekte Adresse mit Username "test" und Password "test123" funktioniert genauso wie mit richtigen Logindaten. hmmmm.
 
Zuletzt bearbeitet:
Hallo Lord

Werde das gleich mal testen, danke für die Hilfe und Geduld mit mir. Ich weiß, dass ich nicht immer der bin, der alles direkt versteht. ^^
Kein Ursache :)

Scheint also zu funktionieren. Einige weiterführende Fragen:
Zu Log: Das ist jetzt im Prinzip das gleiche Ergebnis wie beim System.out, nur dass ich in meiner Klasse am Anfang eine Konstante deklariere und dadurch immer weiß, wo die Meldung ausgegeben wird?
Treffer !

Wenn ich hier jetzt noch weitere Tasks (z.B. Disconnect, Ordner/Datei herunterladen usw) einfügen möchte, kann ich dann die aktuelle Subklasse irgendwie erweitern mit zusätzlichen Funktionen oder muss ich für jede Interaktion mit dem FTP Server einen eigenen Async Task anlegen?
Das musst Du entscheiden , was für Dich besser ist ..
Ich bastel mir normalerweise eigene Tasks , kannst auch mittels Parameter die vorhanden erweitern .
Alles eine Frage der Übersichtlichkeit.

Wobei hier ein Punkt wäre

Der Aufruf :
new _async_ftp_conn("***.**.***.***", "Hier Username eintragen", "Hier Passwort eintragen").execute();
Bedeutet , dass erst dieser Task fertig sein muss, bevor du ihn nochmal , oder einen anderen startest.
Mit dem Aufruf :
new _async_ftp_conn("***.**.***.***", "Hier Username eintragen", "Hier Passwort eintragen").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
erreichst du eine Parallelisierung

EDIT: Aus irgendeinem Grund ist es egal, welche Login-Daten ich eingebe. Hauptsache die Serveradresse stimmt. Korrekte Adresse mit Username "test" und Password "test123" funktioniert genauso wie mit richtigen Logindaten. hmmmm.

Zuletzt bearbeitet: vor 22 Minuten
Dann würde ich nochmal dein PHP oder Deine übergebenen Parameter an den FTP checken .
 
Werde nicht aufgeben, bis das Ding gescheit läuft. Ja, das mit der Parallelisierung wird wohl sinnig sein. Hab ich gerade gemerkt, als ich mich mit einer anderen Serveradresse verbinden wollte. Der Timeout hat sicher 30 Sekunden gedauert (ich natürlich in der Zeit 2 oder 3 mal auf den Button geklickt). Hat dann sehr lange gedauert, bis er die ganze Prozedur 3x durch hatte.
 
Nur so eine Anmerkung nebenbei :

Klar ist es wichtig , dass du dich jetzt in Tasks/Threads einarbeitest - eines der wichtigsten Elemente unter Android.
Allerdings URL/FTP Connections quasi als "Anfänger" zu bewältigen , ist da schon ein hartes Stück Arbeit,
weil es X Zustände geben kann , die du abfangen musst (z.b. connectionlost, timeout, socketlost etc etc)

Selbst ich nehme für sowas lieber die wirklich guten Libraries, bei denen sich die Jungs mit nichts Anderem beschäftigen.
Das kannst du als "Normalo" nicht alles bewältigen

Libraries : Volley, Ion, OkHttp
 
Zuletzt bearbeitet:
@swa00 Jetzt demoralisier mich nicht gleich, hab grad wieder ein wenig reingefunden nach einigen Wochen Pause. :D

Also im allgemeinen bin ich kein Programmieranfänger, arbeite normalerweise mit Datenbankanwendungen. Auf die Androidschiene hab ich mich einerseits aus privatem Interesse begeben und andererseits wäre es cool, wenn man eventuell eine kleine Androidanwendung zur "Unterstützung" der Datenbanken in welcher Form auch immer erarbeiten könnte. Das steht aber noch in den Sternen.

Von daher war ich mir da schon im klaren, dass es nicht ganz so einfach werden würde. Aber ich komme Stück für Stück voran. Zum Glück gibts ja Foren wie dieses, wo man sich immer mal Hilfe von Erfahreneren holen kann. :)
[doublepost=1484137048,1484134160][/doublepost]Kann mir jemand erklären, wieso ich folgenden Error bekomme, wenn ich eine OnPostExecute-Methode anfüge?

Code:
@Override
protected void OnPostExecute(Boolean result) {
    if (result) {
        Log.i(TAG, "Hat geklappt");
    } else {
        Log.i(TAG, "Hat nicht geklappt");
    }
}

Er meckert bei @ Override - Method does not override method from its superclass.

Habe mal bei diversen Suchmaschinen gestöbert, da steht etwas von falschem Rückgabedatentyp. Ist ja aber beides Boolean (Rückgabewert von DoInBackground und Parameter von OnPostExecute).
 
swa00 schrieb:
Siehste, das meinte ich oben mit "kopieren" und deine Parameter sind falsch :)

Du musst schon darauf achten , dass deine ÜbergabeParameter mit den return Parametern übereinstimmen :)

Aus der offiziellen Doku (weil mein Kopf langsam platzt)..
  1. Params, the type of the parameters sent to the task upon execution.
  2. Progress, the type of the progress units published during the background computation.
  3. Result, the type of the result of the background computation.
Ich sende Strings, das ist mir klar. Da ich während die Verbindung aufgebaut wird nichts anzuzeigen habe, zweiter Parameter Void. Als return Value habe ich einen Bool-Wert. <String, Void, Boolean>.. Bin ich zu dumm oder ist es zu einfach?
 
Nee du liegst daneben , die Parameter der Override beziehen sich auf die Parameter , die du ganz oben angibst.
(Siehe die Grafik)
So müssen die Overrides auch deklariert sein und deren Rückgabewerte

_____________________________________________________________________________
HEISSER TIP AN DICH

a) schreib mal nur

Code:
 private class _async_demo extends AsyncTask<Void, Void, String>
{
}

b) dann gehst du mir der Maus auf einen der letzten drei Paramter
c) Dann drückst du CTRL + O
d) Und voila , such dir eine Override aus (doppelklick) und schon bastelt dir AS das Richtige :)
 
Zuletzt bearbeitet:
  • Danke
Reaktionen: lordzwieback
swa00 schrieb:
HEISSER TIP AN DICH

a) schreib mal nur

Code:
 private class _async_demo extends AsyncTask<Void, Void, String>
{
}

b) dann gehst du mir der Maus auf einen der letzten drei Paramter
c) Dann drückst du CTRL + O
d) Und voila , such dir eine Override aus (doppelklick) und schon bastelt dir AS das Richtige :)
Das wars. Android Studio hat mir hier jetzt quasi das gleiche Gerüst, welches ich vorher per Hand getippt habe erstellt. Die einzige Ausnahme war die Zeile "
super.onPostExecute(result);". Habe dann meinen "Testcode" vom alten "onPostExecute" kopiert - funktioniert einwandfrei. Danke für den heißen Tipp ;)

EDIT: Dann haben meine Parameter aber gestimmt. Da habe ich jetzt nämlich nichts verändert. Ich hab mir deine Grafik noch 3x angeschaut gehabt und bin jedes mal zu dem Ergebnis gekommen, dass ich es doch so habe.
 
Dann sollte es wohl nur ein simpler Typo gewesen sein :)
(Achso, super vergessen , stimmt)

Herzlichen Glückwunsch !!

Schau dir trotzdem mal die Libraries an :)
 
Mach ich. Vielen Dank für die Geduld und Hilfe. Ich mach Schluss für heute. Wollte nur nicht mit einem Negativerlebnis morgen wieder an die Sache rangehen, dann ist man irgendwann so deprimiert, dass es keinen Spaß mehr macht. :)
 

Ähnliche Themen

Jansenwilson
Antworten
1
Aufrufe
747
swa00
swa00
M
  • maksimilian
Antworten
4
Aufrufe
986
maksimilian
M
S
Antworten
0
Aufrufe
587
Sergio13
S
Zurück
Oben Unten