Jump to content

Socketverbindung bei Verwendung von WLAN/Ethernet


Recommended Posts

Problem:

 

Wie in diesem Thread zu sehen, bekommt ein Programm welches ausschließlich Callback verwendet und über WLAN betrieben wird ein Verbindungsabbruch von der Gegenseite nicht mit.

 

Dies liegt grundsätzlich erst einmal an der Funktionsweise von Sockets, der "Read Socket" blockiert und bekommt einfach keine neuen Nachrichten mehr und auf den "Write Socket" wird nicht geschrieben. Dadurch kann eine Verbindung nicht automatisch neu aufgebaut werden, da keinerlei Hinweis auf eine Verbindungstrennung vorhanden ist.

 

Ein Workaround habe ich im obigen Thread schon gepostet, Wenn wir regelmäßig den "Write Socket" nutzen (z.B. durch Aufruf eines Getters), bekommen wir mit wenn die Gegenseite wieder da ist und es wird automatisch eine neue Verbindung aufgebaut.

 

Jetzt könnte man diese "Ping Funktionalität" fest in die Bindings einbauen, allerdings stellt mich dies als Lösung nicht zufrieden. Es ist weiterhin so, dass eine Disconnect erst dann aufgerufen wird, wenn die Gegenseite wieder da ist. Nicht aber wenn die Verbindung das erste mal abbricht.

 

 

Problemerklärung:

 

Um zu zeigen, dass dies eine Prinzipielle Eigenschaft von Sockets ist, hier ein Beispielaufbau:

 

PC1 (simuliert WIFI Extension indem ein Socket geöffnet wird und eingehende Daten ausgegeben werden):

# WIFI Extension simulation
import socket

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('', 1234))
s.listen(1)
conn, addr = s.accept()
while 1:
    data = conn.recv(1024)
    print data
conn.close()

 

PC2 (simuliert Client der mit WIFI Extension spricht):

# Client
import socket
import time

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('192.168.178.153', 1234))
while True:
    s.sendall('Hello, world')
    time.sleep(1)
s.close()

 

Wenn ich dieses Programm auf beiden Rechnern starte, gibt PC1 jede Sekunde einmal "Hello World" aus. Wenn ich jetzt das Ethernet Kabel trenne (oder äquivalent die WLAN Verbindung unterbreche), passiert auf beiden seiten _nichts_, Der Server bekommt keine Nachrichten mehr und der Client gibt keine Fehler.

 

Wenn ich jetzt das Programm auf PC2 neustarte und dann das Ethernet Kabel wieder einstecke, bekomme ich dies auf PC1 mit (da "sendall" einen Fehler schmeißt, da die Gegenseite die Socket Verbindung noch nicht kennt). Erst jetzt bekomme ich mit, dass die Gegenseite nicht mehr erreichbar war und kann eine neue Verbindung aufbauen.

 

Exakt das gleiche passiert mit der WIFI Extension, dadurch bekommen wir den Verbindungsabbruch erst mit, wenn die WIFI Extension wieder in Reichweite ist oder sich neugestartet hat o.ä.

 

 

Lösungsvorschlag:

 

Eine mögliche Lösung wäre folgendes: Alle Bindings schicken in regelmäßigen Abständen (alle 2-5 Sekunden?) ein Ping raus. Wenn dies mehr als 3 mal hintereinander passiert, gehen wir davon aus dass die Verbindung getrennt wurde, schmeißen einen DISCONNECTED Callback und versuchen eine neue Verbindung aufzubauen.

 

Vorteil: Eine Verbindungstrennung wird frühzeitig erkannt, nicht erst wenn eine Verbindung wieder aufgebaut werden kann.

 

Der neue Verbindungsaufbau passiert zur gleichen Zeit wie zuvor auch, es gibt dadurch keine Vorteil bzgl. der "Neuverbindungsgeschwindigkeit".

 

Was meint ihr? Ist das den Aufwand Wert? Wird es da zuviele "False Positives" geben (3x Timeout obwohl Verbindung nicht unterbrochen war)?

Link zu diesem Kommentar
Share on other sites

Hmmm?

 

Eine Frage dazu:

Macht es eingentlich einen Unterschied ob die WLAN-Verbindung zum Brick fuer 1 Minuten unterbrochen ist. (Hinternis in der Luft / WLAN AP reboot) oder ob der Brick per Reset neu gestartet wurde? -  Muesste man das nicht unterschiedlich behandeln?

 

---------------------

 

Wer schonmal Monitoring per Ping gemacht, weiss das das nicht richtig funktioniert. Da gibt es immer Unwegbarkeiten.

 

Folgendes Beispiel:

Ping alle 5 Sekunden:

1. No Answer

2. 200ms Reply

3. No Answer

 

Was soll mir soetwas sagen? Wird nach dem ersten beantworteten Ping wieder eine Verbindung aufgebaut?

Ist die Leitung schlecht?

Mir ist nicht so ganz klar wie das richtig helfen soll.

 

---------------------

 

Warum geht es nicht so:

Das Brick sendet die Daten an einen offenen UDP Port der Applikation.

Zusaetzlich werden alle 10 Sekunden (oder variabel) vom Brick ein Beacon versendet, egal wie oft "echte" Daten gesendet werden.

Timer in die Software rein und gut ist.

Deshalb nennen wir es nicht Callback, sondern Callout.  ;)

 

BTW: Wenn man die Idee mit dem UPD Port weglaesst, wuerde dann ein regelmaessiger Beacon nicht schon helfen?

 

Oder habe die ganze Problematik nicht verstanden? ::)

 

Der Loetkolben

 

 

Link zu diesem Kommentar
Share on other sites

Da wir hier von einem Ping über TCP sprechen (korrekt?) würde ich sagen, dass ein einzelner Timeout reichen sollte, um eine Trennung zu erkennen. Immerhin hat ja TCP schon genügend Retransmission-Logik, um einzelne Pakete neu zu übertragen.

 

@Lötkolben: "Normale" Pings laufen über ICMP, auch wenn ich kein Experte dieses Protokolls bin, bin ich mir recht sicher, dass es dort keine automatische Neuübertragung von Paketen gibt. Deswegen würde man dort erst nach einigen Timeouts sagen "jetzt ist die Verbindung kaputt".

 

@automatischer Ping: Würde ich optional aktivierbar machen und den Intervall beim Aktivieren übergeben lassen.

Bei kritischen Anwendungen möchte man zeitnah über einen Disconnect informiert werden, um beispielsweise über eine zweite Sicherheitsebene eine Abschaltung oder ähnliches zu veranlassen. Bei anderen Anwendungen wäre das nur unnötiger Traffic und einmal in der Minute reicht völlig.

 

Was mir noch nicht klar ist:

Du (borg) schreibst, dass auf beiden Seiten nicht bemerkt wird, wenn du das Kabel ziehst. Auf der Seite mit conn.recv kann ich das auch nachvollziehen, aber warum sollte man bei s.sendall nichts mitbekommen? Das hätte ich anders erwartet, werde das aber gleich nochmal testen bevor ich mich hier zu weit aus dem Fenster lehne ^^

 

edit: Meine erste Quelle im Internet gibt mir recht, aber leider funktioniert das Beispiel von borg auch genau so wie er es beschreibt... ich würde erwarten, dass sendall kaputt geht wenn die verbindung abbricht. Habe es gerade nochmal probiert und diesmal etwas länger gewartet. Nach einigen Sekunden wird beim sendall doch ein Fehler geworfen, also alles gut und so wie erwartet.

 

Dementsprechend: Sobald man den Ping über die normale TCP-Verbindung mitlaufen lässt, wird der Port automatisch geschlossen wenn beim Senden etwas schiefgeht. Ob ein oder mehrmals pingen ist also gar nciht unbedingt die Frage. Man muss sich jetzt nur überlegen welche der beiden Parteien aktiv sein soll und welche passiv. Also senden die Bindings regelmäßig einen Ping und die Extension erwartet diesen oder ist es umgekehrt? Die passive Seite muss dann natürlich trotzdem einen Timeout definieren, ab dem sie die Verbindung als tot betrachtet.

Link zu diesem Kommentar
Share on other sites

Habe es gerade nochmal probiert und diesmal etwas länger gewartet. Nach einigen Sekunden wird beim sendall doch ein Fehler geworfen, also alles gut und so wie erwartet.

 

 

Wirklich??? Hast du wirklich das Netzwerkkabel gezogen? Oder hast du eines der Programme mit strg+c beendet? Das macht nämlich einen Unterschied.

 

Network cable unplugged. Any network cables unplugged along the route from one side to the other will cause a loss of connection without any notification. This is similar to the router case; if there is no data being transferred' date=' then the connection is not actually lost. However, computers usually will detect if their specific network cable is unplugged and may notify their local sockets that the network was lost (the remote side will not be notified).[/quote']

 

Ich hab das gerade nochmal ausführlich getestet:

 

Aufbau: Laptop (agiert als WIFI Extension) <-> Ethernetkabel <-> Switch <-> Ethernetkabel <-> PC (agiert als Client).

 

Vorgehen: Skript auf Laptop starten, Skript auf PC starten, Ethernetkabel zwischen Laptop und Switch trennen (nicht das andere, das bekommt das Betriebssystem dann mit), warten...

 

Beobachtung: Das Client Skript läuft solange weiter, bis der TCP Message Buffer des Betriebssystems voll ist, dann blockiert sendall. Das ganze hab ich ca. 30 Minuten so stehen lassen, dann hab ich das Ethernetkabel angeschlossen -> Dann kommen die TCP/IP Nachrichten (der komplette Buffer, es ist nichts verloren gegangen) beim Laptop an.

 

Wenn ich jetzt das gleiche nochmal erzeuge und in der Zwischenzeit das Skript auf dem Laptop neustarte und dann das Ethernetkabel wieder reinstecke (dies ist das Verhalten wenn man die WIFI Extension neustartet), dann bekomme ich einen Socket Fehler (manchmal "Connection reset by peer" und manchmal "Broken Pipe").

 

Der Zeitpunkt, zu dem ich das Ethernetkabel gezogen hab, ist IMO nur durch zählen von Timeouts herauszubekommen. Ich hab auch schon versucht SO_KEEPALIVE und sowas zu setzen, macht keinen Unterschied.

 

 

Edit: Der verlinkte Blogeintrag spricht von der "HTTP Server Methode", hab mir das mal genauer angeguckt. Die Vorgehensweise dort ist folgende: Es wird jedesmal wenn eine Nachricht gesendet/empfangen wird ein Timer wieder auf 0 gesetzt. Wenn der Timer bei einer bestimmten Zeit angekommen ist (z.B. 30 Sekunden?), dann wird einfach die Verbindung geschlossen und neu aufgebaut. Wenn die Verbindung wieder aufgebaut werden kann ist alles gut. Wenn nicht gab es einen Verbindungsabbruch, den wir dann auch per Callback mitteilen können.

 

Was haltet ihr von der Vorgehensweise?

Link zu diesem Kommentar
Share on other sites

@HTTP Server Methode: Das würde ich für den passiven Teil so empfehlen. Also beispielsweise wenn der PC immer an den Stack sendet, dass der Stack dann reseted wenn er nix mehr vom PC hört (für x Sekunden). Theoretisch muss der PC auch nur dann Pings senden wenn sonst nix anderes über die Leitung ging.

 

Zum Thema sendall: Mein Vorgehen war dieses:

PC <-- Kabel --> Router <--- WLAN ---> Laptop

Laptop als Server, PC als Client. Am Laptop WLAN deaktiviert. Jetzt wird auf dem Laptop sofort der Socket geschlossen (logisch, denn das Betriebssystem bekommt ja das abschalten des Netzwerkadapters mit). Auf dem PC dauert es eine Weile (zwischen 5 und 20 Sekunden) bis er schließlich eine Exception wirft. Das ganze unter Windows. Mich wundert es aber, dass dein TCP Stack es offenbar einfach permanent weiterversucht die Daten loszuwerden (bis es dann irgendwann nach 30 Minuten mal wirklich klappt), statt einfach zu melden, dass die versendeten Pakete nicht mehr zugestellt werden konnten (Abbruch).

Umgebung ist bei mir wie gesagt pur Windows, sowohl Client und Server habe ich jeweils in Python 2.7 und C# ausprobiert.

 

edit: Ich habe grad nochmal ein wenig im Internet gesucht, aber ich finde keinen Weg einen Linux oder sonst einen TCP-Stack so zu konfigurieren wie du es beschreibst. Überall lese ich, dass der TCP-Stack nachdem er auf einige ACKs vergeblich wartet der Anwendung mitteilt, dass die Verbindung kaputt ist. Sowohl unter Windows, wie auch unter Linux... Finde das äußerst spannend ^^

Link zu diesem Kommentar
Share on other sites

Ich hab den Client über Nacht laufen lassen und heute morgen hatte "sendall" in der Tat eine Exception geschmissen.

 

Das hier ist aus der .net Dokuemntation, ich finde spezifisch zu Python nichts passendes. Sollte aber allgemein gelten:

 

The successful completion of a send function does not indicate that the data was successfully delivered and received to the recipient. This function only indicates the data was successfully sent.

 

If no buffer space is available within the transport system to hold the data to be transmitted, send will block unless the socket has been placed in nonblocking mode.

 

Soweit ich das verstehe, hat das Betriebssystem einen Buffer für TCP/IP und wenn ich "send" aufrufe, werden Daten in den Buffer kopiert. Sobald sie im Buffer sind bekomme ich zurückgegeben wieviel Byte in den Buffer kopiert wurden. D.h. an der Stelle gibt es keine Garantie das die Daten auch wirklich ankommen.

 

Jetzt ist die Frage wie ich mein Linux hier konfiguriert habe, dass es so lange dauert bis der Kernel mitbekommt das er die Nachrichten nicht los wird und mir den Socket schließt.

 

Ich teste das mal noch mit ein paar anderen Rechnern :)

Link zu diesem Kommentar
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Gast
Reply to this topic...

×   Du hast formatierten Text eingefügt.   Formatierung jetzt entfernen

  Only 75 emoji are allowed.

×   Dein Link wurde automatisch eingebettet.   Einbetten rückgängig machen und als Link darstellen

×   Dein vorheriger Inhalt wurde wiederhergestellt.   Clear editor

×   Du kannst Bilder nicht direkt einfügen. Lade Bilder hoch oder lade sie von einer URL.

×
×
  • Neu erstellen...