Socket select mit unerklärlichen Absturz...



  • Sehr gut, nun schrittweise weiter :

    int SmartSockServer::Run()
    {
     SOCKINFO client;
     char buffer[256];
     char text[256];
     int res;
     int bytes;
     int number;
    
     if((res = socket_accept(server, client)))
      return res;
     else
     {
    // hier "leer"
     }
    
     MessageBox(NULL, "We leave te thread", "Debug", NULL);
     return 0;
    }
    

    Wenns läuft dann :

    ...
     else
     {
      LeaveCriticalSection(&crit);
    
     }
    ...
    


  • OK, auch das funktioniert einwandfrei.

    Hab langsam das gefühl das eine string funktion nicht threadsafe ist, oder vlt doch was anderes?

    Bin jetzt selbst soweit gekommen:

    int SmartSockServer::Run()
    	{
    		SOCKINFO client;
    		char buffer[256];
    		char text[256];
    		int res;
    		int bytes;
    		int number;
    
    		if((res = socket_accept(server, client)))
    			return res;
    		else
    		{
    			LeaveCriticalSection(&crit);
    
    			socket_recv(client, buffer, 256, bytes);
    			buffer[bytes] = '\0';
    			//sscanf_s(buffer, "%i %s", &number, text); //<--- ab der schmiert er ab im Debugbuild!!!
    


  • Hab mal ein wenig nachgelesen:

    char buffer[256];
    char text[256];
    sscanf_s(buffer, "%i %s", &number, text, sizeof(text)); // <-- sizeof fehlte
    

    und jetzt stürzt er vorerst nicht mehr ab ^^. Das sizeof() hab ich aber bisher noch nie wo gesehen... O_o



  • Dann hat also sscanf_s () über die Grösse von "text" hinausgelesen und dadurch die Exception ausgelöst.

    Verhindern lässt sich das durch sizeof () als letzten Parameter ?

    Aha.

    Prüfe unbedingt mal ob sprintf_s () auch solche Überraschungen bereithält. 🙂



  • sprintf_s hat nicht solche überraschungen, zumindstens konnte ich keine entdecken, der sizeof() parameter ist erst mit der secure version von sscanf_s hinzugekommen, ohne Threads und ohne sizeof() gabs irgendwie nie solche exceptions ^^.



  • Euch ist schon klar dass eine CRITICAL_SECTION "owned" ist, also nur von dem Thread aus verlassen (leave) werden darf der sie auch betreten hat (enter)?

    Wenn ja, dann möge mir mal einer erklären was hier abgeht, dann verstehe ich das ganze nicht.

    Wenn nein, dann dürfte wohl genau das das Problem sein.

    EDIT:

    Hab langsam das gefühl das eine string funktion nicht threadsafe ist, oder vlt doch was anderes?

    Natürlich sind string Funktionen nicht threadsafe.
    Die einzigen 2 Dinge die "threadsafe" sind sind 1) Operationen in unterschiedlichen Threads auf unterschiedlichen Strings und 2) ausschliesslich Leseoperationen in unterschiedlichen Threads auf dem selben String.
    Zumindest sind das die Regeln was wahrscheinlich 99% aller Implementierungen der STL bzw. der C-Style String Funktionen angeht (strcpy, strcat, strlen etc.).

    EDIT2: weiters frage ich mich wie wohl socket_recv aussieht -- das riecht auch nach Problemen. Solange die Länge des "Paketes" nicht mitgeschickt wird kann socket_recv nämlich nicht wissen wieviel Bytes ankommen sollen - und wird dann hin und wieder nur einen zu kurzen String empfangen.

    EDIT3: sscanf ist eines der grauseligsten Untiere der C-Lib, würde ich niemals verwenden.



  • socket_recv schickt die länge mit, die steht nämlich in "bytes", und die Funktion ist getestet und funktioniert =)). Ist im groben eine Funktion die die Fehlerbehandlung behinhaltet und nur recv enthält und die maximale puffergröße zum reinkopieren.

    Das mit der CriticalSection werde ich noch verbessern, da sie in der Art nur zum Test drin ist.

    Was würdest du als ersatz für sscanf nehmen?



  • VirtualDreams schrieb:

    socket_recv schickt die länge mit, die steht nämlich in "bytes", und die Funktion ist getestet und funktioniert =)). Ist im groben eine Funktion die die Fehlerbehandlung behinhaltet und nur recv enthält und die maximale puffergröße zum reinkopieren.

    Ok. Wenn du an einem Ende mittels send() 100 Bytes rausschickst, dann garantiert dir keiner dass du am anderen Ende mit dem ersten recv() auch 100 Bytes empfängst. Genausogut könntest du erstmal 1 Byte empfangen, und erst wenn du nochmal recv() aufrufst die restlichen 99.
    Blöderweise passiert das aber nur unter ganz bestimmten Umständen, daher kann es in einem Test ohne sein dass alles funktioniert, obwohl der Code falsch ist, und in gewissen Situationen eben Fehler verursachen kann und wird.

    Wenn du also Strings mit variabler Länge verschickst, dann muss der empfangende Teil irgendwie mitbekommen können wo der String endet. Eine Möglichkeit dafür ist einfach den String mit 0 oder mit "\n" oder sonstwas zu terminieren -- bloss wenn deine Funktion das tun würde, würde sie wohl "socket_recv_string" oder so heissen.
    Eine andere Möglichkeit wäre die Länge des Strings vor dem eigentlichen String mitzuschicken. Funktioniert ganz toll, ist bloss leider nicht für rein textuelle Protokolle geeignet.

    Das mit der CriticalSection werde ich noch verbessern, da sie in der Art nur zum Test drin ist.

    Ich weiss nicht ob du das richtig verstanden hast, aber wenn du "enter" in einem Thread und "exit" in einem anderen machst, dann ist das nicht nur unsauber sondern einfach falsch, und die schlimmsten Dinge können passieren. Du musst das also schon zum Testen richtigstellen, sonst wirst du nämlich nicht vernünftig testen können.

    Was würdest du als ersatz für sscanf nehmen?

    Gute Frage. Einen Parser z.B.
    Bei grösstenteils textbasierten Netzwerkprotokollen (z.B. SMTP, HTTP, FTP, ...) wird man ähnliche Konstrukte wahrscheinlich an vielen Stellen brauchen, also kann es denke ich auch nicht Schaden wenn man sich einen kleinen Parser dafür schreibt, den man dann überall verwenden kann.
    Wie dieser Parser aussieht hängt natürlich wieder davon ab wie das Protokoll aussieht, z.B. wie kompliziert die Syntax ist. Wenn das immer nur so einfache Konstrukte wie "dezimal formatierte Zahl gefolgt von 1-N Whitespaces gefolgt von frei formatiertem String" enthält, dann würde ich vermutlich erstmal die 1-N Whitespaces suchen, den String dort "auseinanderschneiden", dann den ersten Teil als Zahl interpretieren (atoi, strtol, boost::lexical_cast, ...), und den 2. Teil (den frei formatierten String) einfach kopieren.



  • hustbaer schrieb:

    Ok. Wenn du an einem Ende mittels send() 100 Bytes rausschickst, dann garantiert dir keiner dass du am anderen Ende mit dem ersten recv() auch 100 Bytes empfängst. Genausogut könntest du erstmal 1 Byte empfangen, und erst wenn du nochmal recv() aufrufst die restlichen 99.
    Blöderweise passiert das aber nur unter ganz bestimmten Umständen, daher kann es in einem Test ohne sein dass alles funktioniert, obwohl der Code falsch ist, und in gewissen Situationen eben Fehler verursachen kann und wird.

    Das ist mir durchaus bewusst, da es aber über local erst mal gestest wird, geh ich einfach mal davon aus, das auch "100Bytes" ankommen. Da es ja noch im Aufbau ist wird das auch noch geändert und vervollständigt.

    An einem kleinen Parser sitz ich schon, muß nur noch zuende gebracht werden.



  • VirtualDreams schrieb:

    da es aber über local erst mal gestest wird, geh ich einfach mal davon aus, das auch "100Bytes" ankommen.

    Hängt relativ stark von Rechnerauslastung und ein wenig von Random() ab und kommt durchaus öfters auch lokal schonmal vor das nicht alle Bytes in einem Rutsch ankommen 😉
    Ich würde das schon recht früh fixen 😉


Anmelden zum Antworten