Forum: Mikrocontroller und Digitale Elektronik Raspberry - Python: Netzwerk vorhanden?


von Frank E. (Firma: Q3) (qualidat)


Lesenswert?

Ich betreibe mittels Rspberry eine LED-Matrixanzeige, die ihre Inhalte 
per UDP entegen nimmt. Wenn eine Netzwerkverbindung besteht, 
funktioniert Alles einwandfrei.

Zufällig habe ich festgestellt, dass mein Python-Programm sang- und 
kalnglos beendet wird (Und die gesamte Anzeige dunkel bleibt), wenn 
diese eingeschaltet wird, ohne dass eine Netzwerkverbindung besteht. Ich 
hätte für den Fall aber gerne eine Anzeige wie z.B. "no network" oder so 
ähnlich.

Natürlich wollte ich die Initialisierung des UDP-Socket per try/except 
abfangen, funktioniert aber nicht. Ich habe eine Funktion "init()", in 
der ich das proiert habe, wie gesagt, funktioniert nicht. Kann mal bitte 
jemand drübersehen und mit sagen, warum nicht? Danke!
1
def init():
2
   try:
3
      global sock
4
      sock=socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
5
      sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
6
      sock.bind((host, port))
7
      sock.settimeout(10)
8
   except socket.error:
9
      idle(5,"no network - reboot",(255,0,0))
10
      matrix.Clear()
11
      result=os.system("sudo reboot")
12
      sleep(.2)

Idle(...) ist eine Funktion, die ein Testbild mit einer eingebetteten 
Nachricht ausgibt, die funtioniert einwandfrei. Host und Port sind 
globale Variablen, sock wird deshalb "globalisiert", weil weiter unten 
Lese- und Schreiboperationen stattfinden. DAS läuft Alles einwandfrei, 
es geht nur um des Problem "mit ohne Netzwerkinterface" ...

: Bearbeitet durch User
von Markus K. (markus-)


Lesenswert?

Du fängst nur socket.error ab. Was ist mit den anderen Netzwerkfehlern?

: Bearbeitet durch User
von Wolfgang S. (ws01)


Lesenswert?

Frank E. schrieb:

> Natürlich wollte ich die Initialisierung des UDP-Socket per try/except
> abfangen, funktioniert aber nicht. Ich habe eine Funktion "init()", in
> der ich das proiert habe, wie gesagt, funktioniert nicht. Kann mal bitte
> jemand drübersehen und mit sagen, warum nicht? Danke!

Was sagt denn die Konsole? Nun, ich nehme an, Du betreibst das Teil ohne 
Monitor und daher bei fehlendem Netz ohne Diagnosemöglichkeit. Ich löse 
dergl. i.d. Regel so, daß ich den Output des Scripts in eine Datei 
umleite und außerdem alles noch mal in einen try/except Exception,c: 
Block klammere, falls ich im Falle einer unerwarteten Exception mehr als 
einen bloßen Traceback haben möchte.  Hauptziel ist, im Fehlerfall 
genügend Diagnoseinformationen zu bekommen.

Mit lediglich dem obigen Schnipsel läßt sich mehr nicht sagen. Zwar ist 
socket.error Oberklasse von allen Exceptions, die von socket selber 
geworfen werden. Jedoch kann z.B., je nachdem, wie "host" zustandekommt, 
socket durchaus auch andere Exceptions werfen, z.B. produziert 
host="\x00" einen TypeError, der nicht abgefangen wird.

Ohne Netz oder bei jedem sonstigen Fehler in ein reboot-Schleife zu 
gehen kommt mir auch nicht übermäßig klug vor. Der Pi ist ziemlich 
empfindlich, was gewaltsames Abschalten in laufendem Betrieb angeht, und 
anders kommst Du aus so etwas kaum raus.   So lange nicht alles 100%ig 
robust funktioniert, wäre hier das Produzieren von reichlich 
Diagnoseoutput gefolgt von einem sauberen poweroff die bessere Lösung.

von Malte S. (maltest)


Lesenswert?

Wenn die IP, zu der host auflöst, (noch) nicht einem lokalen Interface 
zugewiesen ist, scheitert bind(). Du brauchst IP_FREEBIND oder 
einfacher, wenn nichts dagegen spricht, nimm als host INADDR_ANY.

von Wolfgang S. (ws01)


Lesenswert?

Malte S. schrieb:
> Wenn die IP, zu der host auflöst, (noch) nicht einem lokalen Interface
> zugewiesen ist, scheitert bind().

Das ist richtig. Allerdings wirft das Scheitern einen socket.error, und 
der wird abgefangen.

> Du brauchst IP_FREEBIND oder
> einfacher, wenn nichts dagegen spricht, nimm als host INADDR_ANY.

Bleibt die Frage, ob das das gewünschte Verhalten ist.

von Malte S. (maltest)


Lesenswert?

Wolfgang S. schrieb:
> Das ist richtig. Allerdings wirft das Scheitern einen socket.error, und
> der wird abgefangen.

Richtig. Mir lag auch mehr daran, das Auftreten evtl. ungewollter Fehler 
zu vermeiden.

> Bleibt die Frage, ob das das gewünschte Verhalten ist.

Auch im Sinne, das Skript robust zu kriegen im Falle oft transienter 
Fehler. Zumal sich die Frage stellt, was "Netzwerk da?" bedeutet. Das 
Vorhandensein einer bestimmten(?) lokalen IP hat noch relativ wenig mit 
tatsächlich möglicher Kommunikation zu tun. Ebenso wie das derzeitige 
Nichtvorhandensein von Netzwerkkonnektivität einen Daemon am Start oder 
Weiterlaufen hindern sollte. Daher wenn sich das nicht aus Gründen 
verbietet INADDR_ANY bind()en und ggFs. parallel regelmäßig z.B. durch 
Ping (ICMP oder was unpriviligiertes) eines definierten Ziels die 
Wahrscheinlichkeit des Erreichbarseins überprüfen.

von Frank E. (Firma: Q3) (qualidat)


Lesenswert?

Hallo, danke für die Antworten. Leider bin ich derzeit zu wenig Linux- 
und Python-Experte, um da selber wirklich kreativ zu werden und die Zeit 
drängt auch ein wenig.
Ich habe aber bei meinen Recherchen eine Funktion entdeckt, die mir für 
mein konkretes Problem ausreichend weiterhilft. Ich weiss zwar nicht 
genau, was die eigentlich wie tut (ist ja oft so bei fremdem Code), aber 
sie liefert mir eine Liste der AKTIVEN Netzwerkinterfaces, die ich dann 
entsprechend auswerten kann ("befindet sich eth0 in der Liste und hat es 
eine passende IP"). Und wichtig: Sie schmiert nicht ab!

Wen es interessiert:
1
def interfaces():
2
    is_64bits = sys.maxsize > 2**32
3
    struct_size = 40 if is_64bits else 32
4
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
5
    max_possible = 8 # initial value
6
    while True:
7
        _bytes = max_possible * struct_size
8
        names = array.array("B")
9
        for i in range(0, _bytes):
10
            names.append(0)
11
        outbytes = struct.unpack("iL", fcntl.ioctl(
12
            s.fileno(),
13
            0x8912,  # SIOCGIFCONF
14
            struct.pack("iL", _bytes, names.buffer_info()[0])
15
        ))[0]
16
        if outbytes == _bytes:
17
            max_possible *= 2
18
        else:
19
            break
20
    namestr = names.tostring()
21
    ifaces = []
22
    for i in range(0, outbytes, struct_size):
23
        iface_name = bytes.decode(namestr[i:i+16]).split("\0", 1)[0]
24
        iface_addr = socket.inet_ntoa(namestr[i+20:i+24])
25
        ifaces.append(iface_name,iface_addr)
26
    return ifaces

: Bearbeitet durch User
von Wolfgang S. (ws01)


Lesenswert?

Frank E. schrieb:
> Und wichtig: Sie schmiert nicht ab!

Das glaube ich nicht. Die folgende Zeile kann gar nicht funktionieren.

>         ifaces.append(iface_name,iface_addr)

Ansonsten ist es ja nett, eine Funktion zu haben, die die aktiven 
Interfaces als Tupel aus Name und zugeordneter IP auflistet, indem sie, 
unter Linux laufend, tief in die Eingeweide hineinschaut. Jedoch habe 
ich den Eindruck, daß das zur Konfusion eher noch beiträgt. Aber ich 
geb's auf. Wenn die Botschaft, daß man das plattformunabhängig 
implementieren und testen kann, nicht ankommt, dann ist das eben so.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.