Sporadischer Programm absturzt nach Socket ::recv(...)



  • @SoIntMan sagte in Sporadischer Programm absturzt nach Socket ::recv(...):

    UINT len = 5 + chunk;
    char *buffer = new char[len];
    std::memset(buffer,0,len);
    int res = owner->_tcpClient.Receive(buffer, len);
    
    ....
    
    delete[] buffer;
    

    Warum allokierst du eigentlich vor jedem Aufruf deiner Receive-Funktion abermals neuen Speicher? Nimm doch immer den gleichen Puffer? Dort wird der neue Datenblock reingeschrieben, dann kommt eine übergeordnete Schicht, die die Daten interpretiert und zurück gibt, wieviele Bytes sie davon gebraucht hat. Dann verschiebst du mit memmove den restlichen Datenblock im Speicher um die Anzahl der genutzten Bytes nach vorn.

    Abgesehen davon brauchst du das memset nicht. Was im Speicher steht wird durch Receive sowieso überschrieben.



  • @It0101 sagte in Sporadischer Programm absturzt nach Socket ::recv(...):

    Warum allokierst du eigentlich vor jedem Aufruf deiner Receive-Funktion abermals neuen Speicher? Nimm doch immer den gleichen Puffer? Dort wird der neue Datenblock reingeschrieben, dann kommt eine übergeordnete Schicht, die die Daten interpretiert und zurück gibt, wieviele Bytes sie davon gebraucht hat. Dann verschiebst du mit memmove den restlichen Datenblock im Speicher um die Anzahl der genutzten Bytes nach vorn.
    Abgesehen davon brauchst du das memset nicht. Was im Speicher steht wird durch Receive sowieso überschrieben.

    Das hast du allerdings Recht, auch hier ein gute Hinweis bzw. Denkanstoß (memmove).. danke;)



  • @SoIntMan
    Ich glaube ich sehe dein Problem.

    Du musst genau auf die Rückgabewerte von recv() achten. Der Puffer-Parameter ist aus meiner Sicht etwas missverständlich, da die recv() Funktion bei diesem Parameter nicht auf Nullterminiertheit achtet.

    Ist beispielsweise der Puffer 64 Zeichen groß und recv kann 70 Zeichen einlesen, so schreibt diese 64 Zeichen in den Puffer und gibt 64 zurück. Wenn man diesen nun als C-String interpretiert, knallt es da die Nullterminiertheit fehlt.

    Folgenden Code zum spielen:

    int TcpClient::ReadFromClient(int socket, std::string& buf)
    { 
        char Chunk[64];
    
        buf = "";
        while (true)    // ToDo: Timeout implementieren
        {
            int ret = recv(socket, Chunk, std::size(Chunk), 0); // Chunkweise einlesen
            
            if (ret > 0)
            {
                for  (int i = 0; i < ret; i++)
                {
                    buf += Chunk[i];
                    if (Chunk[i] == '\n')
                        return 42;      // Was tun mit dem Rest? Evt. in zweiten Parameter Rest speichern?
                }
            }
            else if (ret == 0)
            {
                // Behandele "Verbindung geschlossen"
            }
            else
            {
                // Behandele Fehler
            }
        }
        return 4711;
    }
    


  • Das mit dem select ist schon mal inspirierend, probiere bissel rum, ABER hier kachelt mir die App auch ab:
    Es wird ja im select gewartet, bis "daten" da sind, und was passiert den FD_ISSET nicht verstanden, was passiert da hier genau?
    Selbst hier kann mir das Recv abschmieren!!!

    int Receive3(char* buf, int len)
    	{ 
    		int resultCode = 0;
    
    		fd_set read_fd_set;
    		FD_ZERO(&read_fd_set);
    		FD_SET(s_, &read_fd_set);
    
    		/* Initialize the timeout data structure. */
    		timeval timeout;
    		timeout.tv_sec = 1;
    		timeout.tv_usec = 0;
    	
    		//check and wait until data received
    		int selectRet = ::select(s_ + 1, &read_fd_set, NULL, NULL, &timeout);
    			
    		//not data avaiable anymore
    		if (!(FD_ISSET(s_, &read_fd_set))) 
    		{
    			return 0;
    		}
    
    		//socket timeout while waiting for data
    		if (selectRet == 0) 
    		{
    			return -2;
    		}
    
    		//any socket error occurred
    		if (selectRet == SOCKET_ERROR) 
    		{
    			return -1;
    		}
    
    		// recive date from socket
    		int byteSize = ::recv(s_, buf, len, 0);
    
    		//socket gracefully closed
    		if (byteSize == 0) 
    		{
    			return -3;
    		}
    
    		//socket gracefully closed
    		if (byteSize == SOCKET_ERROR) 
    		{
    			return -4;
    		}
    			
    		return byteSize;
    	}
    


  • Hast du die Möglichkeit, die Daten mal nicht zu verarbeiten ,sondern auf Clientseite das Empfangen zwar vorzunehmen, aber mit den Daten erstmal nichts weiter zu tun? Quasi Ausschlussverfahren.

    Und dann im Nachgang die "Verarbeitung" der Daten mal ohne Socket zu testen.



  • Ich hab mal ähnliche Probleme gehabt.
    Wie schon von @It0101 erwähnt, ein Protokoll mit Längenangabe ist sinnvoll.
    Zu beachten ist, dass es sich, auch wenn man Pakete bastelt, immer noch ein Stream gesendet und empfangen wird. Ich hatte mal das Problem, dass im Stream (der ja auch nur eine definierte Länge hat) der Anfang des zweiten Paketes enthalten war…
    Ich habe mir zuletzt damit geholfen, dass ich die Daten in einer lesbaren Form (XML) gesendet, und vor dem Senden vom Server in eine Datei gespeichert habe. Der Client hat die empfangenen Daten auch erst mal in eine Datei gespeichert. Nun konnte ich schon mal durch Vergleichen der Dateien einiges ermitteln.



  • @SoIntMan sagte in Sporadischer Programm absturzt nach Socket ::recv(...):

    int byteSize = ::recv(s_, buf, len, 0);

    Füge doch mal bitte den folgenden Code ein und prüfe das Ganze nochmals im Debug-Modus.

    int byteSize = ::recv(s_, buf, len, 0);
    assert(bytesize < len);
    assert(buf[bytesize] == 0);
    


  • @It0101 sagte in Sporadischer Programm absturzt nach Socket ::recv(...):

    Hast du die Möglichkeit, die Daten mal nicht zu verarbeiten ,sondern auf Clientseite das Empfangen zwar vorzunehmen, aber mit den Daten erstmal nichts weiter zu tun? Quasi Ausschlussverfahren.
    Und dann im Nachgang die "Verarbeitung" der Daten mal ohne Socket zu testen.

    das mach ich auch so, ich allociere nur den puffer (wie oben) und geben ihn wieder frei...
    nach dem Ausschluss-verfahren habe ich den ::recv gemockt und es lief.. alls würde die daten aus recv korrupt sing..
    aber jetzt wo ich schreibe, mache ich doch ein bissel verarbeitung.. das nehm ich mal raus

    @Helmut-Jakoby sagte in Sporadischer Programm absturzt nach Socket ::recv(...):

    Ich hab mal ähnliche Probleme gehabt.
    Wie schon von @It0101 erwähnt, ein Protokoll mit Längenangabe ist sinnvoll.
    Zu beachten ist, dass es sich, auch wenn man Pakete bastelt, immer noch ein Stream gesendet und empfangen wird. Ich hatte mal das Problem, dass im Stream (der ja auch nur eine definierte Länge hat) der Anfang des zweiten Paketes enthalten war…
    Ich habe mir zuletzt damit geholfen, dass ich die Daten in einer lesbaren Form (XML) gesendet, und vor dem Senden vom Server in eine Datei gespeichert habe. Der Client hat die empfangenen Daten auch erst mal in eine Datei gespeichert. Nun konnte ich schon mal durch Vergleichen der Dateien einiges ermitteln.

    ja ich versuche gerade die daten roh (ohne überlagertes framing etc. zu anylsieren)

    @Quiche-Lorraine sagte in Sporadischer Programm absturzt nach Socket ::recv(...):

    Füge doch mal bitte den folgenden Code ein und prüfe das Ganze nochmals im Debug-Modus.
    int byteSize = ::recv(s_, buf, len, 0);
    assert(bytesize < len);
    assert(buf[bytesize] == 0);

    leider kann ich nich debbuggen.. WinCE

    Aber ich könnte die App auch einfach mal wuf win32 kompilieen.. hmm;) und da debuggen..



  • @SoIntMan sagte in Sporadischer Programm absturzt nach Socket ::recv(...):

    leider kann ich nich debbuggen.. WinCE
    Aber ich könnte die App auch einfach mal wuf win32 kompilieen.. hmm;) und da debuggen..

    Ich frage mich welche Art von Daten (Binär, ASCII) du durch recv() empfängst.

    int Receive3(char* buf, int len)

    So ein char* kann leider viele Gesichter annehmen:

    • Binäre Daten
    • Nullterminierter String (String + '\0')
    • String Pool (String + '\0' + String + '\0' + '\0')

    Und wenn der buf Parameter der Receive3() Funktion ein nullterminierter String darstellen soll, so kann es unter gewissen Umständen zu einer fehlende Nullterminierung kommen. Und dann knallt es z.B. wenn du strlen(buf) aufrufst.


    Probiere doch mal ob du den Code geschickt unterteilen kannst, s.d. dieser auf einem Entwicklungsrechner läuft. Dann könnte man einen Stresstest probieren. Die Serverseite könnte man mittels einem einfachen C#/Python Programm simulieren.



  • Guten Morgen ..

    irgndwie habe ich das Gefühtl dass unter WinCE "ws2.lib" und unter Win32 "Ws2_32.lib" das verhalten unterscheidlich ist, da es bei gleicher verwendung von ::recv in wince crashed und in win32 nicht!?



  • Mal ne ganz dumme Überlegung: Wenn recv fehlerhaft wäre, wäre der Fehler nicht schon vor vielen Jahren aufgefallen? Der Fehler muss bei dir liegen. Irgendein Zeiger zeigt nicht wie er soll.



  • Ich weiß nicht, wie du die empfangen Daten verarbeitest oder wie diese gestaltet sind. Aber ich wage mich nochmal mit einer Vermutung raus (habe ich im vorherigen Post nur angedeutet), weil ich mich so an ein Phänomen erinnert fühle.

    Auch wenn von Streams gesprochen wird, wird der ab und an in Teilen übertragen; siehe "tcp packet size".

    Also unter dem einem Betriebssystem habe ich zwei "chunks" (jeweils mit eigenem Header wie Art und Größe der folgenden Daten) nacheinander losgeschickt und es kamen auch zwei IP-Pakete "nacheinander" mit jeweils einem "chunk" an.

    Unter dem anderen Betriebssystem habe ich auch zwei "chunks" nacheinander losgeschickt, und es kamen auch zwei IP-Pakete an, aber eben im ersten IP-Pakete der erste "chunk" und die ersten Bytes des zweiten "chunk".
    Das zweite IP-Paket hatte also nur den Rest des zweiten "chunks" und meine Anwendung hat das natürlich nicht verstanden, weil der Header des zweiten "chunks" nicht vorhanden war.

    Ich habe lange dran herumgedoktert, weil ich lange annahm, das meine "chunks" immer als ganzes beim Client ankommen, was sie ja auch im einem Betriebssystem getan haben. Ich dachte auch lange, dass ich durch das definieren der "tcp packet size" alles in der Hand habe. Stimmte aber im letzteren Fall nicht, das zweite Betriebssystem tat da was es wollte, bzw. sendete bzw. empfing wirklich einen Stream ggf. mit halben oder auch mit mehreren "chunks".

    Bitte entschuldige das krude Deutsch, aber ich wollte das IP-Paket und "mein Paket" verdeutlichen.



  • @SoIntMan sagte in Sporadischer Programm absturzt nach Socket ::recv(...):

    irgndwie habe ich das Gefühtl dass unter WinCE "ws2.lib" und unter Win32 "Ws2_32.lib" das verhalten unterscheidlich ist, da es bei gleicher verwendung von ::recv in wince crashed und in win32 nicht!?

    Weist was, wir probieren es mal auf die andere Weise.

    Probiere doch mal bitte, ob durch den folgenden Code der Fehler behoben wird

    int byteSize = ::recv(s_, buf, len - 1, 0);
    if (byteSize > 0)
      buf[byteSize] = 0;
    

    Ich gehe hier einfach mal davon aus, das du nullterminierte Strings einlesen möchtest und stelle deswegen explizit die Nullterminierung sicher.



  • @Quiche-Lorraine sagte in Sporadischer Programm absturzt nach Socket ::recv(...):

    Weist was, wir probieren es mal auf die andere Weise.
    Probiere doch mal bitte, ob durch den folgenden Code der Fehler behoben wird
    int byteSize = ::recv(s_, buf, len - 1, 0);
    if (byteSize > 0)
    buf[byteSize] = 0;

    Ich gehe hier einfach mal davon aus, das du nullterminierte Strings einlesen möchtest und stelle deswegen explizit die Nullterminierung sicher.

    Hi Danke für dein Engagement, aber leider nein, ich lese reine Binär daten:)

    ist der Buffer den ich ::recv übergebe kleiner als die Daten welche im tcp stack hängen dürfte es ja kein problem geben,
    ist der Buffer größer als die im verfügbaren daten, dann bekomme ich ja die anzahl der gelesen bytes zürück .. d.h. es "müsste" ja gehen... außer ich hab ein mem-bock drin, und win32 is da entspannter als CE (wobei wince6 geh, wince8 nicht)



  • Du musst lernen wie man WinCE Programme remote debuggt


Anmelden zum Antworten