sockets: automatischer reconnect bei netzwerk fehlern etc.



  • Hallo Comunity,

    Ich habe ein Programm geschrieben (Client) welches mehrere Server in einem Netzwerk Periodisch abfragt und von dort Daten erhält. Das Klappt auch alles soweit ganz gut.

    Ich möchte das Programm jetzt so erweitern, das es automatisch eine neue Verbindung aufbaut bzw. wieder herstellt, wenn z.B. ein Netzwerkkabel gezogen wurde/kaputgegangen ist und diese Fehler Quellen beseitigt worden sind.

    Ich wende mich hiermit an dieses Forum da ich hierbei keinen patchwork kode erstellen möchte.
    Ich verwende zurzeit ausschließlich Linux und werde daher auch alle Beispiele in Linux Kompatiblem Code Schreiben. (Hier wäre ich sehr Froh wenn jemand die entprechenden ergänzungen zur Windows Komatibilität machen könnte.) OPTIONAL fürs erste. Desweiteren Beschränke ich mich erstmal auf die Stanadart Sockets mit den alten 32 bit adressen IPV4. (ergänze das evtl später auch noch um IPV6)

    Meine Ziele:
    - Kein "Patchwork" code
    - Besseres Verständnis der Thematik netzwerk Programmierung
    - Kleines Tutorial zum Netzwerkporgrammieren mit Schwerpunkt auf die Fehler erkennung und vermeidung aus sicht des Clients.

    Zu aller erst will ich einmal alles sammel was hierfür nötig ist.
    1. Einführung (einrichten/erstellen eines Sockets etc.)
    3. Was führ Fehler Quellen gibt es? / Welche Fehler können auftreten?
    4. Wie erkenne ich die Fehler richtig?
    4. Wie vermeide/ Behandle ich die Fehler?

    Ich würde mich über Ergänzungen zu den oben genannten Punkten Freuen.

    PS: weitere Ergänzungen folgen in kürze.
    PPS: schon mal vorab ein großes Lob an zotteljedi, vieles von dem was ich mitlerweile über das anwenden von sockets gelernt habe weis ich von seinem Tutorial. Zu finden unter folgender Adresse: http://www.zotteljedi.de/socket-tipps/



  • Und jetzt? Was ist deine konkrete Frage? "Wie lese ich ein Manual?" ?



  • gedult gedult, ich binn kein D-Zug.Kommt alles noch und wiegesagt geht es Primär darum Wie mann die fehler behandlung bei der netzwerk programmierung Richtig realisieret. (für anfänger die sich in dieser Matherie noch nicht so gut auskennen wie mir zum Beispeil ^^)



  • Fehler erkennen: anhand der Returnwerte.
    Fehler vermeiden: Duuuuh!?! Geht nicht? Kabel raus is Kabel raus - wie willst du das vermeiden?

    Und wieso meinst du dass die "Fehlerbehandlung bei der Netzwerkprogrammierung" irgendwie anders sein sollte als in anderen Programmen?



  • Bei Vermeiden meine ich das ganze so programmieren das gewisse Fehler nicht entstehen z.B. an einer bestimmten stelle etwas vergessen abzufragen o.ä. .
    zweitens: Wenn das Problemm gefixt wurde, z.B. Ich bin zum Server gelaufen oder habe jemanden laufne lassen 😃 und die verbindung(Kabel) ist wieder ok, das der Client Automatisch sein vorherige Verbindung korrekt aufbaut (dabei gibt es ja auch einige sachen zu beachten).
    PS: zur anwendung sollte ich wohl noch sagen das Server und Client Nicht im INternet hängen sonder "Lokal" an einem Gebäude netzwerk angeschlossen sind. Deshalb gehe ich hier auch nicht speziell auf IPV6 ein.



  • BabCom schrieb:

    gedult gedult, ...

    Gedult - ist das sowas wie adult?



  • Unter Umständen soll bzw. kann nach einem Verbindungsabbruch nicht einfach die Verbindung wieder hergestellt werden, weil evtl. in der Kommunikationslogik der Zustand verloren gegangen ist. So ein Verhalten lässt sich nicht verallgemeinern, das hängt vollkommen vom Anwendungsprotokoll ab.
    Fehler in der Programmierung vermeidet man, indem man das Manual richtig liest und versteht. Nochmal: Was hast du konkret vor? Das ist ein Forum, hier kommt man meistens mit einer konkreten Frage an.



  • Verbindungs aufbau: mit timeout überwachung

    alle Variablen Bzw. Strukturen sind im Headerfile definiert.

    int socketx::init(string ip, int PORT){
      sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
      if(sock<0)return -1;
    
      server_addr.sin_family = AF_INET;
      server_addr.sin_port = htons(PORT);
      server_addr..sin_addr.s_addr(ip.c_str());
    
      int flags = 0;
      if(flags= fcntl(sock, F_GETFL, 0)<0) return -1;
    
      flags|=O_NONBLOCK;
      if(fcntl(sock, F_SETFL, flags)<0) return -1;
    
      sockRValue = connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
      if(sockRValue<0){
        if(errno == EINPROGRESS){
          FD_ZERO(&fds);
          FD_SET(sock, &fds);
          tv.tv_sec = 15;    //warte nur 15 sec auf verbindungs aufbau
          tv.tv_usec = 0;
          sockRValue = select(sock+1, 0, &fds, 0, &tv);
        }
      }else{
        sockRValue = 1;
      }
    
      flags &= (~O_NONBLOCK);
      if(fcntl(sock, F_SETFL, flags)<0)return -1;
    
      if(sockRValue<0){
        return -1;
      }else if(sockRValue == 0){
        return -1;//timeout abgelaufen
      }else{
        socklen_t len = sizeof(flags);
        if(getsockopt(sock, SOL_SOCKET, SO_ERROR, &flags, &len)<0)return -1;
      }
      return 0;
    };
    

    So baue ich die verbindung auf. hier jetzt die erste richtige frage: Kann man das so machen oder sollte man das lieber anders machen? gibt es gründe die dafür oder Dagegen sprechen?

    Edit: die warte zeiten (tv.tv_sec = 15; tv.tv_usec = 0; ) sind eigentlich werte welche aus einem Konfig file gelesen werden und nicht statisch einprogrammiert wie oben.



  • BabCom schrieb:

    Bei Vermeiden meine ich das ganze so programmieren das gewisse Fehler nicht entstehen z.B. an einer bestimmten stelle etwas vergessen abzufragen o.ä. .

    So wie man es in allen anderen Programmen auch macht. Da ist nix Socket-spezifisches dran.
    Du erwartest aber hoffentlich nicht dass dir hier jmd. so grundlegende Dinge wie Fehlerbehandlung beibringt - bzw. das Programmieren im Allgemeinen.

    zweitens: Wenn das Problemm gefixt wurde, z.B. Ich bin zum Server gelaufen oder habe jemanden laufne lassen 😃 und die verbindung(Kabel) ist wieder ok, das der Client Automatisch sein vorherige Verbindung korrekt aufbaut (dabei gibt es ja auch einige sachen zu beachten).

    Wie Ad aCTa schon geschrieben hat: so "automatisch" geht da gar nix. Wenn die Verbindung abgerissen ist, dann muss dein Programm das mitbekommen, und eine neue aufbauen.

    Und BTW: du bekommst nicht mal automatisch mit dass die Verbindung überhaupt abgerissen ist. Du bekommst das nur mit, wenn du gerade versuchst Daten an die andere Seite zu schicken. Falls du nur wartest dass der andere PC dir was schickt kannst du im Falle des Falles ewig warten.



  • Da ich gestern ein änliches Problem hatte.
    Schreib ich mal den Link mit dem es bei mir geklappt hat

    http://www.binarytides.com/code-a-simple-socket-client-class-in-c/

    und zum ersten testen dann das hier

    sudo nc -lk 443

    hab ich von hier
    http://stackoverflow.com/questions/21873830/open-port-443-by-adding-a-rule-in-iptables-ubuntu

    Um zu du kannst doch wenn einfach gehen soll immer abfragen ob du connecten kannst.

    also die hier abfragen
    -> sockRValue = connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
    wenns nich geht bisschen warten und wieder abfragen...



  • BabCom schrieb:

    ... wenn z.B. ein Netzwerkkabel gezogen wurde/kaputgegangen ist und diese Fehler Quellen beseitigt worden sind.

    1. Link detection geht nur für das eigene Netzwerkkabel, also PC <--> Switch. Hat jedoch nichts mit TCP oder der Verbindung zum Server zu tun.

    2. TCP ist dafür gemacht worden, um auch kurze Unterbrechungen der physikalischen Verbindung, vor allem bei größeren Entfernungen, zu überbrücken und dabei die "geistige" Verbindung zum Gegenüber immer noch aufrechtzuhalten. Die System-Timeouts, die auch eine solche Verbindung als "beendet" erklären und die sich dann erst über den Lesebefehl abfragen lassen, sind jedoch extrem lang (soweit ich weiß ca. 2 Stunden). Vermutlich nicht das, was du willst.

    3. Also: Timeouts selbst verwalten. Wenn der Server eine definierte Antwort auf eine Anfrage zurückschicken muss, definiere eine Zeit, die du ihm gibst (in select() vor dem read() (dann wartet allerdings auch der ganze Thread) oder, falls das Socket nonblocking ist, durch eine entsprechende Schleife).

    4. Ach ja: socket Holen und Einrichten, Verbindungsaufbau sowie Lese- und Schreibaufrufe in eigene Funktionen. Bei einem Verbindungsabbruch muss man nicht das gesamte Socket neu vom System anfordern, es reicht close() und wieder connect() .



  • minastaros schrieb:

    2. TCP ist dafür gemacht worden, um auch kurze Unterbrechungen der physikalischen Verbindung, vor allem bei größeren Entfernungen, zu überbrücken und dabei die "geistige" Verbindung zum Gegenüber immer noch aufrechtzuhalten. Die System-Timeouts, die auch eine solche Verbindung als "beendet" erklären und die sich dann erst über den Lesebefehl abfragen lassen, sind jedoch extrem lang (soweit ich weiß ca. 2 Stunden). Vermutlich nicht das, was du willst.

    Funktioniert nur wenn die "keepalive" Option verwendet wird. Was z.B. bei Windows IIRC per Default nicht so ist.
    (Also bei recv(), bei send() Funktioniert kommt immer irgendwann ein Timeout wenn die Verbindung weg ist.)

    Und nochwas: viele Router haben Timeouts für TCP/IP Verbindungen, die viel kürzer sind als was der TCP Stack üblicherweise so macht. D.h. man muss sowieso eigene (Application-Layer) Keep-Alive Pakete verschicken wenn man nicht möchte dass die Verbindung hopps geht.

    4. Ach ja: socket Holen und Einrichten, Verbindungsaufbau sowie Lese- und Schreibaufrufe in eigene Funktionen. Bei einem Verbindungsabbruch muss man nicht das gesamte Socket neu vom System anfordern, es reicht close() und wieder connect() .

    Kann man machen. Wenn man RAII verwendet is aber eher einfacher alles niederzureissen und komplett neu aufzubauen. Weniger Spezialfälle, weniger Code, weniger Kopfschmerzen. So mach' ich das zumindest in den meisten ähnlichen Fällen (eigentlich so-gut-wie-immer, es sei denn das Niederreissen+Neuaufbauen wäre recht teuer).



  • hustbaer schrieb:

    (Also bei recv(), bei send() Funktioniert kommt immer irgendwann ein Timeout wenn die Verbindung weg ist.)
    ...
    D.h. man muss sowieso eigene (Application-Layer) Keep-Alive Pakete verschicken wenn man nicht möchte dass die Verbindung hopps geht.

    Es dauert nur eben recht lange, bis recv() und send() entsprechende Fehler zurückgeben, zumindest zu lange, wenn es darauf ankommt, innerhalb von wenigen Sekunden eine (physische) Unterbrechung signalisieren zu können. Oder zu lange, wenn man kurz nach dem Schreiben das "Ergebnis" (Status) haben will.

    Richtig, bei send() kann das System die Nachrichten eine Weile puffern und versucht dann seine Retries, bis der Fehler kommt. Auch beim Lesen gibt select() ewig lang grünes Licht, obwohl das Kabel nicht mehr steckt.

    Daher, wie du schreibst: am besten über das Protokoll selbst in die Hand nehmen, wenn man eine schnelle Rückmeldung braucht (das ist glaube ich im Sinne des TE). In einem Fall war genau das das Problem: Das Protokoll war leider so ausgelegt (Vorgabe), dass der Server normale Nachrichten nicht quittiert hat...

    Kann man machen. Wenn man RAII verwendet is aber eher einfacher alles niederzureissen und komplett neu aufzubauen. Weniger Spezialfälle, weniger Code, weniger Kopfschmerzen....

    Meine Socket-Klasse (großes S, ein Wrapper um socket, connect, read usw.) ist auch in gewisser Weise "RAII", zumidnest im globalen Sinne: wird einmal zu Beginn als Instanz erstellt und handhabt die ganze Verbindungslogik intern. Da sie für ein embedded-System geboren wurde, ist mir lieber, nur das zu tun, was wirklich notwendig ist, solange mir der tatsächliche Aufwand des Betriebssystems unbekannt ist. Vielleicht könnte man das sogar vernachlässigen, wer weiß... Aber klar, auf einem großen System kostet den Prozessor das Erstellen eines socket ggf. ein müdes Lächeln.



  • @minastaros
    Nochmal zu Abbruch bei recv() .
    Dabei gibt es per Default nie ein Timeout.
    Es sei denn dein TCP/IP Stack schickt KeepAlive Messages.
    Was wie ich schon geschrieben habe Windows per Default nicht tut. (Andere Systeme möglicherweise schon, keine Ahnung.)

    http://technet.microsoft.com/en-us/library/cc957549.aspx

    By default, keep-alive transmissions are not sent. The TCP keep-alive feature must be enabled by a program, such as Telnet, or by an Internet browser, such as Internet Explorer.

    Muss das Programm also explizit anfordern.

    Und da der TCP/IP Stack ohne KeepAlive Messages nicht mitbekommt dass irgendwas schief gegangen ist, bricht recv() nicht mit einem Fehler ab.

    Und die zwei Stunden die Windows als Default Intervall verwendet sind sowieso ein Witz. Ich kenne keinen Router der ein Connection Timeout >= 2 Stunden hat. Meins hab ich auf wenige Minuten (5? 2? weiss nimmer genau) eingestellt.



  • Hi, das sind schonmal gute anregungen.
    Ich kriege den Verbindungsabbruch früher oder später mit, da die Server Regelmäßing angesprochen werden (Daten angefordert werden). Da die Daten nicht zeitkritischsind, kann ich die Anfragen auch als keepalive packete Benutzen/betrachten? (es ist nicht schlimm wenn ein paar Zyklen keine Daten gelesen werden können).


Log in to reply