C Sockets, Programm funktioniert ein paar mal, dann schlägt read fehl

Fatal Error

Lt. Junior Grade
Registriert
Jan. 2009
Beiträge
294
Hallo Leute,

ich versuche gerade ein Programm in C zu schreiben. Ich habe das erste mal mit C-Sockets zu tun, also glaub ich, dass dieses Problem existiert weil ich einfach keine Ahnung von C habe xD. Prinzipiell ist es ein Client der sich mit dem zugehörigen Server verbindet und Daten über ein eigenes Protokoll austauscht.
Es gibt für diesen Server schon einen Java-Client. Diese Kombination funktioniert auch super. Jetzt wollte ich den Client dazu in C nachbauen.
Die Verbindung funktioniert per TCP. Den Server lasse ich in einer VM laufen (Ubuntu) und der Client läuft auf meinem Macbook. Ich verwende auch nichts anderes als POSIX.
Ich habe jetzt das Problem, dass mein Client ca 5 mal hintereinander funktioniert, und dann liefert eine der read-Methoden einen Fehler. Im Wireshark sehe ich aber, dass der Server seine Daten korrekt überträgt. Ich hab auch schon sowas wie setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &true, sizeof(int)) und setsockopt(sock, SOL_SOCKET, TCP_NODELAY, (char *)&flag, sizeof(int)) probiert, aber immer wieder das gleiche. Es passiert auch nicht immer beim selben read.
Nach ein paar Minuten gehts wieder einmal kurz.

Habt ihr eine Idee was das sein könnte?
Eventuell, dass der Server die Verbindung aus irgendeinem Grund schließt und ich deshalb am Client nichts mehr bekomme?
Es wird wahrscheinlich irgendwas mit TCP zu tun haben was ich übersehe.
Wahrscheinlich ist es irgendein Anfängerproblem, aber auch nach langem Suchen komme ich auf keine Antwort...

Falls jemand nach Sourcecode fragt...es ist wirklich nichts besonderes. Es wird abwechselnd read und write aufgerufen. Da müsste auch alles stimmen, weil wenn es mal funktioniert dann funktioniert das alles.

Danke schonmal für eure Ratschläge.

Edit:

strerror(errno) gibt folgendes aus im Fehlerfall: "Resource temporarily unavailable"
Manchmal kommt aber auch "Undefined error: 0"
Vielleicht hilft das schonmal, ich such auch weiter.
 
Zuletzt bearbeitet:
Aus meiner eigenen Erfahrung mit C kann ich nur sagen, der Teufel liegt oft im Detail.
Deswegen denke ich, wird das hier ohne SourceCode eher ein Blick in die Kristallkugel als wirkliche Hilfe für dich.
Kann es sein, dass der Socket irgendwo geschlossen wird?
Versuche um das Problem weiter einzugrenzen dir soviel Debugausgaben wie möglich zu schreiben. Oft sieht man dadurch, dass ein Wert nicht stimmt, oder dass der Fehler an ganz anderer Stelle liegt, als er sich am Ende zeigt
 
Würde ich auch sagen, ohne SourceCode sehr schwierig zu beantworten.
Und es handelt sich um C? Nicht um C++?
 
Ja es handelt sich um C.
Ok etwas genauer: Das eigene Protokoll funktioniert so, dass einfach Zeilen geschickt werden. Also eine Zeichenkette und dann ein \n. Dazu gibts auch eine readline-Methode, die eben so oft read ausführt bis ein \n erkannt wurde. Nachdem ich die einfach vom Server Sourcecode kopiert hab, und dieser ja ohne Probleme funktioniert vermute ich den Fehler woanders, aber um sicher zu gehen:

Code:
#define NEWLINE '\n'

int readline(int sock, const char *buffer, int size) {

    const char *buffer_pointer = buffer;
    int size_remaining = size;
    int r;
    char *newline_pointer = NULL;
    
    do {
        r = read(sock, (void *)buffer_pointer, size_remaining);
        //r = recv(sock, (void *)buffer_pointer, size_remaining, 0);
        
        ////////////////
        if (r <= 0) {
            printf("read err: %s\n", strerror(errno));
        }
        ////////////////
        
        if (r > 0) {
            newline_pointer = memchr(buffer_pointer, NEWLINE, r);
            buffer_pointer += r;
            size_remaining -= r;
        }
    } while (r > 0 && newline_pointer == NULL && size_remaining > 0);
        
    if (newline_pointer != NULL) {
        *newline_pointer = '\0';
    }

    if (size_remaining <= 0) {
        return -1;
    }
    
    return  buffer_pointer - buffer;
}

Diese Methode sollte einwandfrei funktionieren. Wenn es mal funktioniert, dann funktioniert wie gesagt das ganze Programm. Wenns nicht funktioniert bekomm ich bei strerror(errno) "Undefined error: 0" zurück. Ich tipp immer noch darauf, dass irgendwas vom Betriebssystem nicht freigegeben wird oder so :/

Im Programmablauf rufe ich das dann so auf:

Code:
#define OK_NL "OK\n"

    /* write "OK" */
    write(sock, OK_NL, sizeof(OK_NL) - 1);
    //send(sock, OK_NL, sizeof(OK_NL) - 1, 0);
    
    // receive "XYZ"
    char buf1[4096];
    int r;
   
    if ((r = readline(sock, buf1, sizeof(buf1))) <= 0)
    {
        printf("err: readline returned %d\n", r);
        close(sock);
        exit(EXIT_FAILURE);
    }

Und das halt mehrmals hintereinander. Es bleibt auch nicht immer an der gleichen Stelle stehen, also nicht immer beim gleichen read/readline. Laut Wireshark hat der Server aber immer die nächste Zeichenkette geschickt, mein read empfängt nur nichts...
 
Wo wird der Socket erzeugt? Hast du ihn vlt als Non-Blocking angelegt, oder einen Receive-Timeout gesetzt?
Was passiert, wenn du den Fehler ignorierst, und einfach weiter versuchst, Daten einzulesen?
 
Ich frage zuerst mittels getaddrinfo die IP ab und erzeuge dann ganz normal mit der ersten funktionierenden Adresse einen Socket. Non-blocking ist er nicht (ich weiß nichtmal wie ich auf non-blocking setzen kann). Ich habs mit und ohne Receive/Send und sonstigen Timeouts/Einstellungen wie TCP_NODELAY probiert, aber immer das selbe Ergebnis.

Du meinst wenn ich im Fehlerfall einfach mal nochmal readline aufrufe?
 
Ja, momentan bricht die Schleife ja ab, wenn r==0 ist. Also vlt mal probieren, wenn du einfach immer weiter read() aufrufst.
 
das programm hat so wie es ist mehrere probleme:
1. du erkennst einen rückgabewert des read's von 0 als abbruchfehler. das sollte nicht sein.
2. du gehst davon aus, dass du immer einge ganze zeile ausliest, inklusive dem NEWLINE zeichen.
das ist ebenfalls falsch. du hast keine garantie darüber, wie zerstückelt die daten mit read ausgelesen werden.

wenn deine zeile beispielsweise sehr lang ist, dann wird sie ggf. in zwei packete unterteilt.

letzten endes muss du eine eigene stream-read methode schreiben, die quasi nichts anderes macht als einen buffer zu füllen. den buffer musst du dann selbst nach einen NEWLINE parsen und erst, wenn du an's ende des buffer kommst weiter aus dem stream lesen.
so grob in etwa.

edit: nochmal zu read rückgabewert von 0. das bedeutet nur, dass er 0 bytes gelesen hat. dafür kann es aber verschiedene gründe geben: z.b. weil es im moment nichts gibt, oder weil der read durch einen iterrupt unterbrochen wurde, etc.

du musst schon checken, was da los ist.
 
Zuletzt bearbeitet:
Zurück
Oben