[gelöst] SDL_net "free(): invalid pointer:" Fehler
-
Hi,
seit längerer Zeit schreibe ich mir nun schon eine kleine Gameengine, welche auf SDL, SDL_net, SDL_image, SDL_mixer und OpenGL basiert. Das, was ich für 2D Spiele benötige ist außer zwei drei Kleinigkeiten komplett fertig. Nur der Netzwerkpart über SDL_net will nicht so ganz. Momentan habe ich ein simples System, in dem nur ein lokaler Server UDP erlaubt ist und die Clients broadcasten Pakete an den Port des Servers, auf welche dieser wieder antwortet. So werden die IP-Adressen ausgetauscht. Ob und wie man über TCP broadcastet weiß ich nicht, deshalb baut TDP auch zuerst eine UDP-Verbindung zum IP-Tausch auf und wechselt anschließend erst auf TCP. Ob das ganze bisher funktioniert ist noch nicht klar, da ich es aufgrund des im Topicnamen genannten Fehlers noch nicht testen konnte.Infos zum Fehler
Unter Linux erhalte ich folgende Fehlerausgabe:*** glibc detected *** ./2DTest: free(): invalid pointer: 0x002d673c ***
Dieser Fehler tritt immer dann auf, wenn ich einen UDP-Clienten erstellt habe und diesen wieder lösche.
Nach meinen Google-Recherchen versuche ich delete bzw. delete[] falsch aufzurufen, aber ich rufe weder new, noch delete auf, höchstens eine SDL_net-Funktion könnte einen für sie ungültigen Zeiger erhalten, aber ich steige nicht dahinter welche. Im Prinzip entspricht mein Quelltext den Internettutorials, welche ja funktionieren müssten. Ich habe nur wenige Abweichungen.Hier ist meine Netzwerkklasse, in der alle Verbindungen erstellt werden usw.
#ifndef GL_NET #define GL_NET #define LINUX #ifdef LINUX #include <SDL/SDL.h> #include <SDL/SDL_net.h> #else #include <SDL.h> #include <SDL_net.h> #endif #include <string> class GLNet { public: GLNet(); ~GLNet() {EndConnection(); SDLNet_Quit();} //Verbindungsaufbau bool TCPServer(); bool TCPClient(); //UDP bool UDPServer(); bool UDPClient(); //Weitere Funktionen void setPort(int port) {Port = port;} int getPort() {return Port;} void setPacketSize(int laenge); //Verteilerfunktionen, welche je nach Verbindungstyp passende Funktion aufrufen void frame(); //Für Verbindungsaufbau usw. void EndConnection(); //Je nach Verbindungsart, diese Verbindung beenden void Send(void *daten, int laenge); //Je nach Verbindungsart, über diese senden void Recv(void *daten, int maxlaenge); //Je nach Verbindungsart, über diese empfangen //Individuelle und allgemeine Kennung void setName(std::string name) {Name = name;} std::string getName() {return Name;} void setProgrammName(std::string programmName) {ProgrammName = programmName;} std::string getProgrammName() {return ProgrammName;} //Verbindung bei Spieleintritt nicht weiter suchen usw. void setState(int state) {State = state;} int getState() {return State;} int getClients() {return Clients;} private: //Funktionen für internen Gebrauch, damit nicht falsch ausgewählt werden void EndTCPServer(); void EndTCPClient(); void EndUDP(); //TCPServer void TCPServerSend(void *daten, int laenge); void TCPServerRecv(void *daten, int maxlaenge); //Daten empfangen //TCPClient void TCPSend(void *daten, int laenge); void TCPRecv(void *daten, int maxlaenge); //Daten empfangen //UDPServer void UDPServerSend(void *daten, int laenge); //UDPClient void UDPSend(void *daten, int laenge); bool UDPRecv(void *daten, int maxlaenge); //Server-/Clientliste IPaddress Adresses[128]; std::string Names[128]; std::string Name; //Eigener Name im Netzwerk std::string ProgrammName; //Name der Anwendung um im Netzwerk von anderen Programmen zu unterscheiden IPaddress Adresse; //Immer nur temporär benötigt int Port; //Der zu benutzende Port bei TCP Verbindungen TCPsocket Server; TCPsocket Client[128]; SDLNet_SocketSet ClientSet; //Liste aller Clients zum parallelen abrufen int Clients; //Anzahl Clients UDPsocket UDPSocket; UDPpacket* Packet; int State; /*Status der Verbindungen: 0 = nicht verbunden, 1 = TCPServer, 2 = TCPClient, 3 = UDPServer, 4 = UDPClient, 5 = UDPServer/Listening, 6 = UDPClient/Broadcasten, 7 = TCPServer als UDPServer/Listening 8 = TCPClient als UDPClient/Broadcasten, 9 = TCPClient connecten, 10 = TCPServer/Listening, 11 = TCPServer/Listening abschließen(Socketset), */ }; #endif
Die dazugehörige cpp ist schon etwas länger, deshalb kürze ich erstmal alles raus, was nicht mit UDP-Clients zutun hat:
#include "GLNet.h" #define LINUX #ifdef LINUX #include <SDL/SDL.h> #include <SDL/SDL_net.h> #else #include <SDL.h> #include <SDL_net.h> #endif #include <string> GLNet::GLNet() { SDLNet_Init(); //< 0 == Fehler //Wichtige Variablen initialisieren Port = 1000; //Standardport, kann später geändert werden Server = NULL; for(int i = 0; i < 128; i++) { Adresses[i].host = NULL; Adresses[i].port = NULL; Client[i] = NULL; Names[i] = ""; } Clients = 0; UDPSocket = NULL; State = 0; } //etc. bool GLNet::UDPClient() { //Alte Verbindung beenden EndConnection(); UDPSocket = SDLNet_UDP_Open(0); //Nächsten freien Port auswählen, sicherer, dass funktioniert und Server erhält Clientports in Paketen if(UDPSocket == NULL) return false; Packet = SDLNet_AllocPacket(1024); //Standardgröße, kann später geändert werden //Status aktualisieren State = 6; return true; } void GLNet::EndUDP() { SDLNet_UDP_Close(UDPSocket); UDPSocket = NULL; SDLNet_FreePacket(Packet); Clients = 0; for(int i = 0; i < 128; i++) { Adresses[i].host = NULL; Adresses[i].port = NULL; Names[i] = ""; } //Status aktualisieren State = 0; } void GLNet::EndConnection() { switch(State) { case 0: return; break; case 1: EndTCPServer(); break; case 2: EndTCPClient(); break; case 3: EndUDP(); break; case 4: EndUDP(); break; case 5: EndUDP(); break; case 6: EndUDP(); break; case 7: EndTCPServer(); break; case 8: EndTCPClient(); break; case 9: EndTCPClient(); break; case 10: EndTCPServer(); break; case 11: EndTCPServer(); break; } } void GLNet::frame() { if(State == 5) { //UDP-Server Listening //Versuchen, einen Client zu akzeptieren if(SDLNet_UDP_Recv(UDPSocket, Packet) == 1) { bool SecondTime = false; //Checken ob dieser Client schon aufgenommen wurde for(int i = 0; i < Clients; i++) { if(Packet->address.host == Adresses[i].host && Packet->address.port == Adresses[i].port) { SecondTime = true; break; } } if(!SecondTime) { Adresses[Clients].host = Packet->address.host; Adresses[Clients].port = Packet->address.port; std::string Temp = (char*)Packet->data; if(Temp.find(ProgrammName) == std::string::npos) //Abbrechen wenn Nachricht nicht von gleichem Programm return; //stammt std::string Name = Temp.substr(ProgrammName.length()); //Erst ProgrammName, dahinter Username Names[Clients] = Name; Clients++; } //Sender antworten, damit Senden einstellen kann Adresse.host = Packet->address.host; Adresse.port = Packet->address.port; Packet->address.host = Adresse.host; Packet->address.port = Adresse.port; std::string End = "Accepted"+ProgrammName+Name; const char* PEnd = End.c_str(); Packet->data = (Uint8*)PEnd; Packet->len = End.length(); } } if(State == 6) { //UDP-Client broadcasten //Eigene IP-Adresse broadcasten Adresse.host = INADDR_BROADCAST; Adresse.port = Port; Packet->address.host = Adresse.host; Packet->address.port = Adresse.port; std::string Temp = ProgrammName+Name; const char* PTemp = Temp.c_str(); Packet->data = (Uint8*)PTemp; Packet->len = Temp.length(); SDLNet_UDP_Send(UDPSocket, -1, Packet); //Auf Serverantwort warten und dann Status ändern if(SDLNet_UDP_Recv(UDPSocket, Packet) == 1) { Adresses[0].host = Packet->address.host; Adresses[0].port = Packet->address.port; std::string Temp = (char*)Packet->data; if(Temp.find(ProgrammName) == std::string::npos || Temp.find("Accepted") == std::string::npos) return; //Abbrechen wenn Nachricht nicht von gleichem Programm stammt std::string TempB = Temp.substr(8+ProgrammName.length()); Names[0] = TempB; //Status aktualisieren State = 4; } } //etc. } //etc.
Die Sende- und Empfangsfunktionen habe ich jetzt auch erst einmal eingespart, da der Fehler ja schon auftritt, sobald ich nur einen UDP-Client erstelle anschließend EndConnection(); aufrufe.
Wenn es weiter hilft, kann ich noch die Windowsfehlermeldung ansehen, aber wahrscheinlich kommt da außer "hat einen Fehler verursacht und muss beendet werden" nicht viel rüber.
Hat jemand eine Idee was es sein könnte, bzw. sieht den Fehler vielleicht schon? Wäre echt dankbar, da ich selbst im Moment ein wenig ratlos bin.
Viele Grüße,
Little Programmer
-
Da beim Beenden einer UDP Verbindung ja nur der UDPSocket geschlossen wird und der Speicher vom Paket wieder freigegeben wird, habe ich mir mal die Adresse des Zeigers auf das Paket ausgeben lassen.
Diese ist bei meinem Testlauf als Dezimalzahl 2, sobald ich mein Netzwerkobjekt erstellt habe. Starte ich einen Server, bleibt die Speicheradresse auf 2, obwohl dort dem Zeiger ein neuer wert zugewiesen werden müsste. Starte ich einen Clienten, erhalte ich eine gigantische negative Zahl, könnte der maximale negative Integerwert sein. Und wenn ich diesen Speicherplatz dann freigeben will, tritt der Fehler auf.
Bleibt nur noch die Frage, wieso SDL_net beim Speicher reservieren dem Zeiger solch einen schrägen Wert zuordnet. Ich werd jetzt noch ein wenig herumexperimentieren in der Hoffnung, dass die Lösung vom Himmel fällt.
-
So, Stand der Dinge:
Hatte nen belegten Port für den Server gewählt, jetzt geht er korrekt erstellen.
Erstelle ich einen UDP-Server, gibt er dem Paket eine gültige Adresse und erstellt einen gültigen Socket. Erstelle ich einen UDP-Clienten passiert das gleiche, das Paket erhält halt eine andere Adresse. Rufe ich nachdem ich einen Server erstellt habe EndConnection(); bzw. EndUDP(); auf, funktioniert es einwandfrei, rufe ich es auf, nachdem ich einen Client erstellt habe, stürzt das Programm ab.
Das macht irgendwie nicht viel Sinn für mich.
-
Merkwürdig ist auch die Tatsache, dass entweder beide Speicheradressen, also wenn ich einen Server oder einen Clienten erstelle, negativ oder positiv sind. negative Speicheradressen dürfte es ja nicht geben. Aber bei manchen Programmstarts erhält der Zeiger nur negative Wert und bei manchen positive. Aber egal ob negativ oder positiv, nur wenn ich den Paketspeicher des Clienten freigebe stürzt er ab, beim Server beschwert er sich nicht.
-
Fehler gefunden.
Das UDPpacket* Packet;
ist ein Zeiger auf ein struct, welches wieder einen void* Zeiger namens data auf Daten enthält. Ich habe beim Broadbasten immer temporäre char* Arrays verwendet und im Paket verknüpft. Am Ende der Funktion wurde das Array dann gelöscht und der Zeiger im Paket blieb. Beim löschen des Paketes wollte SDL_net auch die mit data verlinkten Daten löschen und scheiterte daran. Man muss den data-Pointer nach dem Senden nur immer wieder auf 0 setzen, dann ist alles in Ordnung.