ftp übertragung hängt sich auf

Sockets, TCP/IP, (XML-)RPC und ähnliche Themen gehören in dieses Forum
Antworten
gugi
User
Beiträge: 8
Registriert: Dienstag 4. März 2014, 12:15

Hallo!
Ich bin neu hier und habe bis jetzt noch wenig Erfahrung mit Python gemacht.
Ich habe auf meinem Raspberry ein Script geschrieben welches mir alle 5 minuten kleine Textdateien auf einen FTP-Server schreibt. Das ganze funktioniert soweit sehr gut und macht das was es soll. Allerdings habe ich ungefähr einmal die Woche das Problem das sich das Script mitten in der Übertragung aufhängt. Ich kann genau sehen wie ein Teil einer Textdatei fehlt. Das ganze wäre nicht schlimm wenn es irgendwann zu einem Abbruch kommen würde den ich mit Try-Except abfangen kann, tut es aber nicht. Das Script ist wie eingefrohren. Ich muss dann immer einen Neustart durchführen. Dummerweise läuft der Raspberry nicht bei mir zuhause.
Mein Code zum Übertragen schaut so aus:

Code: Alles auswählen

def ftp5min():
        try:
                con = urllib2.urlopen("http://www.google.com/") #internet testen
                data = con.read()
                print "---------------------  online -------------------------"
                
        #mit ftp verbinden und hochladen
                sftp = ftplib.FTP('ftp.xxxx.de','user','pass') # Connect
        #datei hochladen
                fp = open('slg/datei1.txt','rb') # file to send
                sftp.storbinary('STOR ' +'httpdocs/PI/datei1.txt', fp) # Send the file
                fp = open('slg/datei2.txt','rb') # file to send
                sftp.storbinary('STOR ' +'httpdocs/PI/datei2.txt', fp) # Send the file

                fp.close() # Close file and FTP
                sftp.quit()
                print 'Die FTP-Verbindung wurde getrennt.'

        except:
                print "   "
                print "   -----  Fehler oder offline :-(   --------"
Jetzt habe ich von einem socket-timeout gelesen:

Code: Alles auswählen

import socket
socket.setdefaulttimeout(20.0)
Meine Frage ist nun, fängt dieser Timeout das Problem ab?
Es ist für mich schwer diesen Fehler zu simulieren, deswegen würde ich mich freuen wenn jemand eine Idee hat.

Christoph
BlackJack

@gugi: Du rufst tatsächlich alle fünf Minuten die Google-Startseite ab?

Statt ``print`` würde ich das `logging`-Modul vorschlagen. Da kannst Du die Ausgaben dann auch gleich so konfigurieren, das vor jeder Ausgabe ein Zeitstempel ausgegeben wird, und auch gleich in eine Logdatei schreiben lassen. So lässt sich dem Problem vielleicht einfacher auf die Spur kommen wenn Du eine Logdatei hast wo a) jede Ausgabe auch tatsächlich rausgeschrieben wird und nicht vielleicht noch im Dateipuffer verbleibt, und b) eine Zeit zu sehen an der das Programm hängen geblieben ist. Dann kann man schauen ob das mit irgendwelchen Ereignissen in anderen Protokolldateien korelliert, oder immer zu einer bestimmten Zeit passiert, oder etwas in der Art.

Die erste Datei wird nicht explizit wieder geschlossen. Da verwendet man in der Regel die ``with``-Anweisung um das Schliessen zu garantieren.

Ein nacktes ``except:`` ohne eine konkrete Ausnahme ist keine gute Idee. Die Information *das* ein Problem aufgetreten ist, hilft nicht wirklich weiter solange man nicht weiss *was* das für ein Problem war. In dieser Ausnahme liegt vielleicht der entscheidende Hinweis warum Dein Programm hängen bleibt. Auch hier wäre `loggint` wieder hilfreich, denn Logger haben eine Methode um Ausnahmen zu protokollieren.

Es wäre vielleicht eine gute Idee mit ``try``/``finally`` dafür zu sorgen, dass die `quit()`-Methode vom `FTP`-Objekt auch auf jeden Fall aufgerufen wird, also auch wenn zwischen dem Verbinden und dem Abbau der Verbindung eine Ausnahme auftritt.
gugi
User
Beiträge: 8
Registriert: Dienstag 4. März 2014, 12:15

Hallo!
Ja das mache ich, was spricht da dagegen? Ich habe gemerkt das mein Programm keine Probleme macht und sicher in das Except läuft wenn ich an dieser Stelle schon kein Internet bekomme. Deswegen verwende ich es so.
Das Logging-Modul ist bei mir fast überflüssig weil ich in den Dateien ebenfalls die Zeitstempel mit dokumentiere, ist somit denk ich doppelter Aufwand. Kurz vor der Übertragung werden Werte mit dem Zeitstempel gespeichert. Ich kann auch gleich sagen das die Uhrzeiten querbeet sind. Ich sehe auch genau das das Problem nicht schon beim Verbinden zum Server besteht sondern erst wirklich bei der Übertragung mitten drin (anhand der halb übertragenen Dateien). Mag sein das ich die erste Datei nicht extra geschlossen habe, ist aber an dieser Stelle nicht das Problem, sonst würde die Übertragung nicht starten, oder liege ich da falsch?
Du hast sicher recht das das einfache Except etwas ungenau ist, stimmt. Kommt daher das ich ein Python-Anfänger bin und mich schon gefreut habe das ich überhaupt Fehler abfangen kann :-) . Auch hier liegt aber das Problem nicht darin das ich den Abbruchgrund nicht unterscheiden kann sondern darin das ein Abbruch der durch Except gefangen werden könnte gar nicht erst stattfindet. Das ist eigentlich mein Hauptproblem welches ich lösen möchte. Sollte nämlich eine Übertragung nicht vollständig durchlaufen macht mir das nichts aus weil 5 min später ja schon wieder übertragen wird... So wäre mir mit einer funktionierenden Except-Falle mit der ich dann im Programm weiter unten einfach weiter machen kann geholfen.
Try finally ist mir neu, muss ich mich erstmal schlau lesen. Greift das Finally auch dann wenn es zu keiner Ausnahme kommt? (was bei mir wohl der Fall ist)

Christoph
BlackJack

@gugi: Du erzeugst automatisierten unsinnigen Traffic für Google. Nett ist was anderes.

Wenn das wirklich mitten beim Übertragen der Datei hängt, dann würde ich mal sagen das ist kein Python-Problem. Du könntest beim `FTP`-Objekt Debugausgaben mit der `set_debuglevel()`-Methode aktivieren. Und vielleicht schauen ob ein Timeout hilft. Den dann aber vielleicht nur beim `FTP`-Objekt und nicht generell für alle Sockets. Spätestens dann wäre aber das ``except:`` zu generell, denn man möchte dann ja schon ganz gerne wissen *warum* in den Zweig gegangen wird. Da möchte ich noch mal `logging` ins Spiel bringen, denn wie gesagt haben Logger eine Methode um Ausnahmen zu protokollieren, inklusive Traceback. Das Logging am Anfang einmal mit `basicConfig()` zu konfigurieren, sich einen Logger für das Modul geben zu lassen, und dann jedes ``print`` durch einen entsprechenden Methodenaufruf auf dem Logger zu ersetzen, würde ich jetzt auch nicht unbedingt als riesigen Aufwand sehen. Und beim nächsten Programm das Protokollausgaben macht dann gleich mit `logging` anfangen. ;-)

Dateien kann man nicht beliebig viele öffnen ohne das sie wieder geschlossen werden, da hat das Betriebsystem eine Grenze, zumindest für einzelne Prozesse. Darum wird sich in den meisten Fällen die Speicherbereinigung von Python rechtzeitig kümmern, aber verlassen sollte man sich halt nicht darauf. Das ist ein Fehler im Programm, auch wenn der nicht oder nur selten auftritt, es ist halt nicht garantiert dass das so auf Dauer gut geht. Und der Fix — die Datei auch wieder zu schliessen — ist ja jetzt nichts kompliziertes oder aufwändiges.
gugi
User
Beiträge: 8
Registriert: Dienstag 4. März 2014, 12:15

Ich sags mal so, lieber mehr traffic und dafür ein Programm das sich nicht aufhängt als andersrum. Ist zugegeben sicher nicht toll, aber ich konnte mir bisher nicht anders helfen.
Ich sehe das auch so das das nicht an Python liegt. Ich habe den Raspberry über Powerlan mit dem Internet verbunden. Hier vermute ich das es gelegentlich mal kurz aussetzt, was im ungünstigsten Fall beim Übertragen passiert. Nur ich denke das Python, zumindest so wie ich es programmiert habe, nicht damit umgehen kann. Ich habe den gleichen code in meinem Keller laufen wo ich seit bestimmt 6 Monaten noch nie einen Absturz hatte.
Du schreibst grad was von Timeout. Das ist genau das was ich auch gedacht habe. Kannst du mir vielleicht sagen wie ich den in meinem Fall am besten und einfachsten einbinden kann? Dieser Socket.Timeout den ich gefunden habe ist so wie ich das verstanden habe für alle sockets. Weist du wie ich den nur für FTP aktiviere? Wenn ich das richtig gesehen habe gibt es eine Möglichkeit beim Verbindungsaufbau, aber bei der Übertragung? Sollte es mit Timeout funktionieren dann könnte ich mir denk ich auch die Google-Abfrage schenken was auf jeden fall sauberer wäre.
Dieses logging klingt interessant :-) . Hast du da zufällig ein Beispiel zur Hand? Dann verstehe ich das vielleicht am einfachsten. Aber da muss ich nochmal dumm fragen: Wenn es zu keinem Abbruch kommt weil der Code einfach einschläft, wird dann trotzdem was ins logging geschrieben?
Zu dem Datei-Close Thema, wäre es in meinem Fall dann einen Möglichkeit das fp.close() einfach nach jeder übertragenen Datei auszuführen?

Danke fürs mitdenken :-)
Sirius3
User
Beiträge: 17750
Registriert: Sonntag 21. Oktober 2012, 17:20

@gugi: Timeouts kannst Du einfach beim __init__ von FTP übergeben. Die macht übrigens keine 5 minütiges FTP, der Name ist also irreführend. Die Kommentare sind weitgehend sinnfrei.

Code: Alles auswählen

import os
import time
import logging
import ftplib

def sendfiles(host, user, password, files, timeout=60):
    sftp = ftplib.FTP(host, user, password, timeout=timeout)
    try:
        for filename in files:
            with open(filename,'rb') as fp:
                sftp.storbinary('STOR httpdocs/PI/%s' % os.path.basename(filename), fp)
    finally:
        sftp.quit()

def main():
    while True:
        try:
            sendfiles('ftp.xxx', 'user', 'pass', ('slg/datei1.txt', 'slg/datei2.txt'))
        except:
            logging.exception("sendfiles failed")
        time.sleep(300)
            
if __name__ == '__main__':
    main()
gugi
User
Beiträge: 8
Registriert: Dienstag 4. März 2014, 12:15

Danke für das super Beispiel!
Den Timeout werd ich mal mit einbauen. Ich lese zu dem Thema immer das ein Standard Timeout eingestellt ist wenn nichts extra angegeben wird. Kennst du zufällig den wert davon? Weil, wenn ein Wert hinterlegt ist, dann müsste doch auch so irgendwann mal ein Timeout kommen.

Try-finally hab ich jetzt denk ich begriffen, is auch nicht schlecht. Aber bei meinem speziellel Fall würde mir das nicht weiterhelfen oder? Wenn keine Ausnahme auftritt bei der der Code abbricht sondern nur stehen bleibt kommt es auch nicht mehr zum finally oder?
Das Logging ist ja auch easy, wenn ich das vorher schon gewusst hätte :-) . In deinem Beispiel bekomme ich aber nur Info auf dem Bildschirm oder wird das gleichzeitig noch irgendwo abgespeichert?
BlackJack

@gugi: Die `FTP`-Klasse setzt bei ihren Sockets kein Timeout wenn man keinen Wert beim erstellen angibt.

Wenn man nichts weiter Konfiguriert werden die Loggingausgaben auf der Standardfehlerausgabe des Prozesses ausgegeben. Eine einfache Möglichkeit das zu ändern ist die `logging.basicConfig()`-Funktion.
gugi
User
Beiträge: 8
Registriert: Dienstag 4. März 2014, 12:15

Ok, dann macht das wieder Sinn.
Das logging schau ich mir nochmal an.

Danke für deine Tipps! Ich werde berichten ob ich Erfolg habe oder nicht :-)
gugi
User
Beiträge: 8
Registriert: Dienstag 4. März 2014, 12:15

Kleiner Zwischenstand, ich habe versucht durch manuelles trennen der NW-Verbindung während der Übertragung die Situation zu simulieren. Genau nach der eingestellten Zeit kam jedesmal der Timeout und der Code hat eine Ausnahme erzeugt wodurch er in Except gesprungen ist. Scheint zu funktionieren :-)
Wenn jetzt der Langzeittest auch noch so gut aussieht wäre mein Problem behoben!
Danke nochmal!
Antworten