[gelöst] "Speicherzugriffsfehler" beim Schreiben in eine Datei



  • D3lta schrieb:

    Der String wird so gesendet:

    string s="test"
    send(s.c_str(), s.size());
    

    Also sollte er Nullterminiert sein...

    mfg D3lta

    P.S.: Wie würdest du das denn überprüfen?

    Nö. size() ist hier 4, weil es die 0 nicht mitzählt.



  • D3lta schrieb:

    Der String wird so gesendet:

    string s="test"
    send(s.c_str(), s.size());
    

    Also sollte er Nullterminiert sein...

    Erstens ist der gesendete String nicht nullterminiert (string::size() liefert die Größe in Nutzzeichen ohne das (im Umgang mit reinen C++ Strings nicht unbedingt erforderliche) \0 am Ende. Und zweitens Ist es viel wichtiger, was recv() am anderen Ende entgegennimmt.

    P.S.: Wie würdest du das denn überprüfen?

    Ich vermute mal, daß recv() die Anzahl der empfangenen Zeichen zurückliefert. Wenn das so ist, mußt du sie nur merken und das \0 selber dahinterschreiben:

    const Socket_Server& Socket_Server::operator>> (string& s) const
    {
        try
        {
            char buffer[MAX_DATASIZE+1];
            int recv_count = Socken::recv(bufferm, MA_DATASIZE);
            if(recv_count == 0)
                throw CONNECTION_END;
            buffer[recv_count] = '\0'; // hier wird der Null-Terminator gesetzt
            log->write("Daten erhalten");
            s = buffer;
            log->write("Vom Client empfangen:");
            log->write(s);
        }
        catch(Exception e)
        {
            throw Exception(e.get_error(), log);
        }
        //catch(CONNECTION_END) {throw;}
        return *this;
    }
    


  • CStoll schrieb:

    D3lta schrieb:

    Der String wird so gesendet:

    string s="test"
    send(s.c_str(), s.size());
    

    Also sollte er Nullterminiert sein...

    Erstens ist der gesendete String nicht nullterminiert (string::size() liefert die Größe in Nutzzeichen ohne das (im Umgang mit reinen C++ Strings nicht unbedingt erforderliche) \0 am Ende. Und zweitens Ist es viel wichtiger, was recv() am anderen Ende entgegennimmt.

    P.S.: Wie würdest du das denn überprüfen?

    Ich vermute mal, daß recv() die Anzahl der empfangenen Zeichen zurückliefert. Wenn das so ist, mußt du sie nur merken und das \0 selber dahinterschreiben:

    const Socket_Server& Socket_Server::operator>> (string& s) const
    {
        try
        {
            char buffer[MAX_DATASIZE+1];
            int recv_count = Socken::recv(bufferm, MA_DATASIZE);
            if(recv_count == 0)
                throw CONNECTION_END;
            buffer[recv_count] = '\0'; // hier wird der Null-Terminator gesetzt
            log->write("Daten erhalten");
            s = buffer;
            log->write("Vom Client empfangen:");
            log->write(s);
        }
        catch(Exception e)
        {
            throw Exception(e.get_error(), log);
        }
        //catch(CONNECTION_END) {throw;}
        return *this;
    }
    

    Danke für die Antwort, aber es hat nicht geholfen... 😞

    mfg D3lta

    P.S.: Ich habe herausgefunden, dass der Fehler zwischen dem Annehmen des Clients (was erfolgreich zu verlaufen scheint) und dem ersten Befehl in dem überladenem operator>> liegt (also eventuell ein Syntaxfehler beim operator?



  • Du brauchst ein (Framing) Protokoll - d.h. ein Mechnismus um den Anfang und das Ende einer Übermittelten Nachricht (bei dir Strings) festzustellen und entsprechende zu extrahieren.

    Eine Möglichkeit ist (wie bereits halbwegs angefangen) die Strings als C-Strings zuu versenden, d.h. inkl. terminierendem 0. Auf der Empfangsseite empfängst Du solange Zeichen bis ein 0 kommt - voila, das ist das Ende des Strings und du hast deine Nachricht Empfangen. Ev. sind im selben recv(..) Aufruf noch mehr Strings vorhanden (hintereinander).

    Allgemeine Möglichkeiten fürs Framing:
    - Nachrichten (Frames) haben fixe Länge
    - Nachrichten haben einen Header mit fixer Länge, in dem Header steht die Länge der Payload
    - Markierung durch spezielle Zeichen vom Ende einer Nachricht - alles dazwischen ist Payload (bei Dir ist das 0).

    So, nun musst Du noch wissen, dass 1 send(..) Aufruf der 16 Zeichen übermittelt nicht unbedingt in 1 recv(..) Aufruf resultiert - sondern z.B. in zwei, einmal 14 bytes und einmal 2 bytes. Desshalb brauchst Du einen Zwischen Puffer für die Empfangenen Zeichen, welche aber noch keine komplette Nachricht bilden.

    Das alles gilt für den Fall, dass deine Wrapper des Sockets einfach send(..) und recv(..) wrappen - d.h. keine weitere Semantik ins Spiel bringen.

    Simon



  • theta schrieb:

    Du brauchst ein (Framing) Protokoll - d.h. ein Mechnismus um den Anfang und das Ende einer Übermittelten Nachricht (bei dir Strings) festzustellen und entsprechende zu extrahieren.

    Eine Möglichkeit ist (wie bereits halbwegs angefangen) die Strings als C-Strings zuu versenden, d.h. inkl. terminierendem 0. Auf der Empfangsseite empfängst Du solange Zeichen bis ein 0 kommt - voila, das ist das Ende des Strings und du hast deine Nachricht Empfangen. Ev. sind im selben recv(..) Aufruf noch mehr Strings vorhanden (hintereinander).

    Allgemeine Möglichkeiten fürs Framing:
    - Nachrichten (Frames) haben fixe Länge
    - Nachrichten haben einen Header mit fixer Länge, in dem Header steht die Länge der Payload
    - Markierung durch spezielle Zeichen vom Ende einer Nachricht - alles dazwischen ist Payload (bei Dir ist das 0).

    So, nun musst Du noch wissen, dass 1 send(..) Aufruf der 16 Zeichen übermittelt nicht unbedingt in einem recv(..) Aufruf resultiert - sondern z.B. in zwei, einmal 14 bytes und einmal 2 bytes. Desshalb brauchst Du einen Zwischen Puffer für die Empfangenen Zeichen, welche aber noch keine komplette Nachricht bilden.

    Das alles gilt für den Fall, dass deine Wrapper des Sockets einfach send(..) und recv(..) wrappen - d.h. keine weitere Semantik ins Spiel bringen.

    Simon

    So etwas hatte ich noch vor in die Serverklasse zu integireren und das Logging ist im Moment noch katastrophal 😉
    Aber wie will ich so etwas machen, wenn ich noch nicht einmal 4 Zeichen übermittelt kriege 😕

    mfg D3lta

    P.S.: Danke für die Inspirationen für das Framing 👍



  • Aber wie will ich so etwas machen, wenn ich noch nicht einmal 4 Zeichen übermittelt kriege

    Mach das Framing und es geht - das bedeutet nämlich dass recv(..) sovielmal aufgerufen wird, bis mindestens eine komplette Nachricht empfangen wurde.

    ➡ recv(..) in einer Schleife aufrufen, bis komplette Nachricht oder sogar mehrere Nachrichten empfangen wurden!



  • theta schrieb:

    Aber wie will ich so etwas machen, wenn ich noch nicht einmal 4 Zeichen übermittelt kriege

    Mach das Framing und es geht - das bedeutet nämlich dass recv(..) sovielmal aufgerufen wird, bis mindestens eine komplette Nachricht empfangen wurde.

    ➡ recv(..) in einer Schleife aufrufen, bis komplette Nachricht oder sogar mehrere Nachrichten empfangen wurden!

    Bei mir ist der Buffer aber 1024 Zeichen groß, da passen die paar Zeichen doch Sicher hinein, oder?

    mfg D3lta

    P.S.: Außerdem liegt der Fehler noch vor dem Aufruf von recv() !

    mfg D3lta



  • Ich vermute mal, dass du in deinen anderen Klassen, wie hier auch, die Return-Codes nicht prüfst. Alle Socket-Funktionen geben -1 zurück, wenn etwas nicht geklappt hat. Ich würde das überprüfen und im Fehlerfall eine Exception mit aussagekräftiger Nachricht werfen - Dann siehst du sofort, wo es kracht.



  • Bei mir ist der Buffer aber 1024 Zeichen groß, da passen die paar Zeichen doch Sicher hinein, oder?

    Ja, sie passen potentiell rein. Das heisst noch lange nicht dass der recv(..) Aufruf soviele reinschreibt. Wieviel genau reingeschrieben werden sagt dir der Return Wert von recv(..). Achtung: Auch Fehler oder Connection Ende (wie schon in deinem Code behandelt) wird über den Return Code mitgeteilt.

    P.S.: Außerdem liegt der Fehler noch vor dem Aufruf von recv() !

    Wo?

    [EDIT]
    Ausserdem:

    Socket::send((void*)s.c_str(), s.size());
    

    Der Cast unnötig, weil die Konvertierung nach void* implizit geht. Gilt auch für andere Stellen.

    catch(Exception e)
    

    Exceptions soll man üblicherweise per (const) Referenz fangen.



  • theta schrieb:

    Bei mir ist der Buffer aber 1024 Zeichen groß, da passen die paar Zeichen doch Sicher hinein, oder?

    Ja, sie passen potentiell rein. Das heisst noch lange nicht dass der recv(..) Aufruf soviele reinschreibt. Wieviel genau reingeschrieben werden sagt dir der Return Wert von recv(..). Achtung: Auch Fehler oder Connection Ende (wie schon in deinem Code behandelt) wird über den Return Code mitgeteilt.

    P.S.: Außerdem liegt der Fehler noch vor dem Aufruf von recv() !

    Wo?

    D3lta schrieb:

    ...

    P.S.: Ich habe herausgefunden, dass der Fehler zwischen dem Annehmen des Clients (was erfolgreich zu verlaufen scheint) und dem ersten Befehl in dem überladenem operator>> liegt (also eventuell ein Syntaxfehler beim operator?

    314159265358979 schrieb:

    Ich vermute mal, dass du in deinen anderen Klassen, wie hier auch, die Return-Codes nicht prüfst. Alle Socket-Funktionen geben -1 zurück, wenn etwas nicht geklappt hat. Ich würde das überprüfen und im Fehlerfall eine Exception mit aussagekräftiger Nachricht werfen - Dann siehst du sofort, wo es kracht.

    Doch, ich prüfe bei jedem Aufruf auf den RetVal -1, aber in der class Socket, auf welcher diese hier basiert. 🕶

    mfg D3lta



  • Und wie kommst Du darauf, dass es vor dem 1. recv(..) ist?
    Und wenns so ist, dann debugge doch einfach.



  • theta schrieb:

    Und wie kommst Du darauf, dass es vor dem 1. recv(..) ist?
    Und wenns so ist, dann debugge doch einfach.

    Durch Debug-Meldungen 😉

    mfg D3lta



  • D3lta schrieb:

    Doch, ich prüfe bei jedem Aufruf auf den RetVal -1, aber in der class Socket, auf welcher diese hier basiert. 🕶

    Und du wirfst schön brav socket_error's?



  • 314159265358979 schrieb:

    D3lta schrieb:

    Doch, ich prüfe bei jedem Aufruf auf den RetVal -1, aber in der class Socket, auf welcher diese hier basiert. 🕶

    Und du wirfst schön brav socket_error's?

    Ja, mit meiner Exception-Class 😃
    Aber es wird keine geworfen...

    mfg D3lta



  • D3lta schrieb:

    theta schrieb:

    Und wie kommst Du darauf, dass es vor dem 1. recv(..) ist?
    Und wenns so ist, dann debugge doch einfach.

    Durch Debug-Meldungen 😉

    mfg D3lta

    Ich würds durchsteppen.



  • theta schrieb:

    D3lta schrieb:

    theta schrieb:

    Und wie kommst Du darauf, dass es vor dem 1. recv(..) ist?
    Und wenns so ist, dann debugge doch einfach.

    Durch Debug-Meldungen 😉

    mfg D3lta

    Ich würds durchsteppen.

    Ich benutze den gcc-Kompiler 😃
    Außerdem habe ich mit Debug-Meldungen den Fehler weitesgehend ausgemacht, ich makiere den Fehler gleich im 1. Post.

    mfg D3lta

    EDIT: makiert 🙂



  • Ich habe weiter nach dem Fehler gesucht und verdächtige nun Socket_Server::accept(), den Fehler zu verursachen, deswegen habe ich den Startpost mit meinem aktuellen Source versehen und den Fehler in die main.cpp geschrieben.

    mfg Δ3lta

    P.S.: Gibt es in dem Forum hier so eine Art Spoiler (Womit man Text verstecken kann, aber bei Mausklick sichtbar wird)? Ich habe so etwas bisher noch nicht gefunden...


  • Mod

    D3lta schrieb:

    P.S.: Gibt es in dem Forum hier so eine Art Spoiler (Womit man Text verstecken kann, aber bei Mausklick sichtbar wird)? Ich habe so etwas bisher noch nicht gefunden...

    Nein, wozu? Wenn du den Lesefluss nicht durch unnötig lange Codestücke unterbrechen möchtest, dann benutz' externe Pastebins. Die kann man zur Not auch als Ersatz für "echte" Spoiler benutzen.



  • SeppJ schrieb:

    D3lta schrieb:

    P.S.: Gibt es in dem Forum hier so eine Art Spoiler (Womit man Text verstecken kann, aber bei Mausklick sichtbar wird)? Ich habe so etwas bisher noch nicht gefunden...

    Nein, wozu? Wenn du den Lesefluss nicht durch unnötig lange Codestücke unterbrechen möchtest, dann benutz' externe Pastebins. Die kann man zur Not auch als Ersatz für "echte" Spoiler benutzen.

    Hätte ja sein können, dass es so etwas gibt...

    mfg D3lta



  • Wenn Du ein minimales Bsp. machst, das den Kompiliert, Linkt und den Fehler zeigt tu ichs dir debuggen...


Anmelden zum Antworten