[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.


Anmelden zum Antworten