C++ Client-Socket crashed wenn Router 24 std/dc durchführt

Hallo liebe Programmierer.

Ich habe einen IRC Chat-Clienten geschrieben. Alles funktioniert soweit wunderbar.
Nur leider stürzt das Programm ab, wenn mein Plaste-Router (DSL) den 24 stündlichen disconnect durchführt.

Der lokale Client-Socket bekommt nicht mit, wenn der Router ne neue IP Adresse bekommt.
Der Socket ist für mein Programm noch valid, getsockopt() liefert keinen Fehler.
Die Störung offenbart sich erst dann, wenn nen String gesendet wird - das Programm dann einfach abstürzt.

Ich habe schon alles mögliche versucht, den Fehler irgendwie abzufangen, leider ohne Erfolg.

Ich könnte das Programm via Script über eine unendlich Schleife nach dem crash neu starten lassen, aber das ist doch irgendwie so, als würde man ein Pflaster drüber kleben...

Ich könnte auch ne Routine erstellen, die die externe IP auf Änderung prüft, und gegebenfalls nen neuen Socket erstellt, aber ich glaube es gibt bestimmt eine bessere Lösung...

Ich bin echt ratlos, google liefert nichts brauchbares darüber.
Bitte helft einem Laien und Hobby-Programmierer, sonst fallen ihm noch mehr Haare aus. :)

Hier noch meine send Routine, falls es wichtig ist.

Code:
void SendIRCmsg(){
    SocketCheck = getsockopt (Socket, SOL_SOCKET, SO_ERROR, &Error, &Errlen);
    if (SocketCheck != 0) NotConnected = true;
    if (Error != 0) NotConnected = true;
    if (!NotConnected){
        fcntl(Socket, F_SETFL, O_WRONLY);
        SocketState = send(Socket, IRC_Pld, strlen(IRC_Pld), 0);
        if (SocketState == -1) NotConnected = true;
        else{
            sprintf(Sockinout, "out");
            fcntl(Socket, F_SETFL, O_NONBLOCK);
            for (LinePtrArr = 0; IRC_Pld[LinePtrArr] != '\0'; LinePtrArr ++){
                if (IRC_Pld[LinePtrArr] == '\n') IRC_Pld[LinePtrArr] = '/';
                if (IRC_Pld[LinePtrArr] == '\r') IRC_Pld[LinePtrArr] = '/';
            }
        }
    }
    UpdateonNext = true;
}
 
Mit was für einer Meldung stürzt denn der Client ab?

Mal davon ab das die Formatierung grotte ist und der Code unvollständig(so weiss ich zB nicht ob NotConnected so ok ist.
das letzte NotConnected=true wird von der Funktion nicht ausgewertet.

Dein Problem ist ein gutes Beispiel wiso man mittels try-catch-block Socketfunctionen einkapseln sollte und dann kann man im catch-Block das Problem bearbeiten.
Gruß

Fluffy
 
Der lokale Client-Socket bekommt nicht mit, wenn der Router ne neue IP Adresse bekommt.
Der Socket ist für mein Programm noch valid, getsockopt() liefert keinen Fehler.

Du musst/solltest zwischendurch checken ob die Verbindung noch besteht. Das ist ein Unterschied zu einem Socket, denn der ist, streng genommen, ein Filedescriptor und solange valid bis er zerstört wird (egal ob die Connection steht).

Du kannst das mit "recv()" hindengeln und so prüfen, ob die Verbindung noch besteht (0 heisst idR tot). Das könntest du über einen Timer/Interrupt machen. Das fragst du dann regelmäßig ab. Exception handling @Fluffy, muss da ohnehin rein.

Auch das "Blocked/non-Blocked Sockets" sollte dich interessieren.
 
Kann man so auch machen, aber ich würde die Verbindung nicht mittels Intervall überprüfen, da das zu Raceconditions führen kann.

Bei sowas ist man IMHO mit dem KISS-Prinzip gut aufgehoben, da man den Code so schreiben kann als wenn die Verbindung besteht was den Fluss der Logik einfacher begreifbar macht, und bei einer Exception, in dedizierter Methode/Funktion auswerten und dann nochmal senden.

Bei Blocked/Nonblocked bin ich mir nicht so sicher, da das Blocking ja ggf. gewollt ist, aber der Punkt ist müßig da wir zu wenig über das Programm wissen.
Gruß

Fluffy
 
Bei sowas ist man IMHO mit dem KISS-Prinzip gut aufgehoben, da man den Code so schreiben kann als wenn die Verbindung besteht was den Fluss der Logik einfacher begreifbar macht, und bei einer Exception, in dedizierter Methode/Funktion auswerten und dann nochmal senden.

Man will ja auch in Abwesenheit empfangen - insofern wäre eine EXP nur beim Versand nicht ausreichend.

Will man es selber bauen, so richtet man sich Signalhandler und Timer (alarm()) ein, die im Kern die Connection mittels recv() prüfen. Das ist schon ziemlich KISS und robust.

Eine andere (eher ungeile) Möglichkeit sind noch Socketoptionen wie SO_KEEPALIVE in Zusammenarbeit mit select() - allerdings liegt hier der Timer idR bei 2 Stunden und wird global verwaltet. Könnte man zwar ändern - ist aber nicht schoen.

Bei einem solchen Design ist das primäre Ziel, den Zustand der Verbindung zu ermitteln und gleichzeitig die Fähigkeiten des Stacks zu berücksichtigen.


Nachtrag: Du kannst natürlich auch extern deine eigene IP abfragen und schauen, ob die sich geändert hat und ggf. die connection neu aufbauen :)
 
Vielen Dank für die Infos. Werde eure Beiträge genau studieren.

Edit:
nicht blockierender Socket, liefert bei recv() immer -1 wenn Socket leer ist.
Eigentlich wollte ich "Nicht-blockierend" nutzen, ist irgendwie bequemer,...
allerdings scheint das wohl dilettantisch zu sein.

Werde mich nun mit select() beschäftigen, um den Fehler mit recv() abfangen zu können - Ich vermute, dass das Programm dann nicht crashed. Und ne ordentliche Exeption-Behandlung kommt auch rein...
 
Zuletzt bearbeitet:
mit Select... gelöst.

Hier noch mal die Lösung, falls es interessiert. Ich glaube jeder der sich damit beschäftig, wird an diese Hürde stoßen:

Nicht blockierender Socket liefert beim recv() immer -1, außer es liegen Daten vor. Es Stürzt bei Fehler nicht ab...

Deshalb muss vor send(), select() genutzt werden. Edit: nur bei Linux afaik
Ich weiss, Exeption könnte besser sei, aber es läuft nun rund wie nen Bienchen... ich lass es erstmal so.
Hier mal Code mit erklärung zu select()

Code:
void SendSockMsg(){
    SocketState  = getsockopt (Socket, SOL_SOCKET, SO_ERROR, &Error, &Errlen);
    if (SocketState != 0) Connected = false;
    if (Error != 0) Connected = false;
    if (Connected){
        timeout.tv_sec = 0;  // muss in Schleife, da Select-Aufruf das Set somit Timeout ändert ...
        timeout.tv_usec = 0;
        FD_ZERO(&Socket_fd); // Set wird gelöscht...
        FD_SET(Socket, &Socket_fd); // Set Muster wird dem file descriptor hinzugefügt
        SocketState = select(1, NULL, &Socket_fd, NULL, &timeout); // Set wird auf Änderungen geprüft,... falls "nicht geschrieben werden kann...
        if (SocketState != -1){
            if (!FD_ISSET(Socket, &Socket_fd))  //... liefert ISSET != 0
                SocketState = send(Socket, Pld, strlen(Pld), 0);
            if (SocketState == -1) Connected = false;
            for (LinePtrArr = 0; Pld[LinePtrArr] != '\0'; LinePtrArr ++){
                if (Pld[LinePtrArr] == '\n') Pld[LinePtrArr] = '/';
                if (Pld[LinePtrArr] == '\r') Pld[LinePtrArr] = '/';
            } // rturn löschen für weitere Bearbeitung
        } else Connected = false;
    }
    UpdateOnNext = true;
}
Korrektur oder Meinung willkommen... :)
 
Zuletzt bearbeitet:
Zurück
Oben