[TServerSocket] Empfangspuffer für jeden verbundenen Client



  • Hallo,
    ich hab ne Frage bezüglich der Paketpuffer bei einem Serversocket. Mir fallen bisher 2 Lösungswege dazu ein:

    1. Jeder am Server verbundene Client bekommt einen separaten Puffer worin die empfangenen Pakete abgelegt werden bis das Telegram vollständig ist und abgearbeitet werden kann. Wie könnte man dynamisch diese Puffer allokieren, im besten Fall als std::vector<unsigned char>

    2. Ein globaler Empfangspuffer in Form eines std::vector als Klassenmember worin alle von den Clients stammenden Pakete abgelegt werden. Senden z.B. 2 Clients gleichzeitig, würden doch die Pakete wild gewürfelt in dem globalen Puffer landen. Um diese später als ganzes Telegram zusammensetzen zu können, könnte man doch die SocketHandle ID jedem Paket voranstellen und danach aneinandersetzen. Der Vector könnte dann wie folgt aussehen: 1125:Paketdaten1127:Paketdaten1125:Paketdaten

    Bisher habe ich als Basis folgendes:

    void __fastcall TForm1::ServerSocket1ClientRead(TObject *Sender,
    	  TCustomWinSocket *Socket)
    {
    
    	std::vector<unsigned char> ReceiveBuffer(Socket->ReceiveLength(), 0 );
    
    	if( !ReceiveBuffer.empty() )
    	{
    		int Received = Socket->ReceiveBuf( &ReceiveBuffer[0], ReceiveBuffer.size());
    		if( Received > 0 )
    		{
    		  ServerPaketsammler.insert( ServerPaketsammler.end(), ReceiveBuffer.begin(),ReceiveBuffer.begin() + Received );
    		  ServerHandleReceivedPackets(Socket->SocketHandle);
    
    		}
    	}
    
    }
    

    Dies mag bei einem einzelnen Client funktionieren aber sobald 2 Clients Daten senden und alle Pakete im Vector "ServerPaketsammler" landen, tritt das Szenario wie unter 2. beschrieben ein. Am liebsten hätte ich für jeden Client einen eigenen Empfangspuffer. Möglich wäre es doch sicher auch pro Verbindung einen TMemoryStream zu erstellen. Ein weiteres Problem die Zuordnung des Puffers an seine Clientverbindung. Evtl. wäre es hier sinnvoll die TMemoryStreams in einer TObjectList zu verwalten und dann irgendwie mit der SocketHandle ID zu kennzeichnen.

    Hat jemand von euch einen heissen Tip für mich wie man dies elegant lösen könnte?



  • Endlich ist der Groschen gefallen und habe die Instanzierung der Puffer über eine Klasse gelöst. Für jede Clientverbindung erzeuge ich mit new eine neue Klasseninstanz und lege deren Zeiger in Socket->Data ab:

    Klasse:

    class ClientSession
    {
       public:
    
       private:
       int SocketHandle;
       bool Authenticated;
       std::vector<unsigned char> Paketsammler;
    
       friend class TForm1;
    
    };
    

    OnClientConnect:

    void __fastcall TForm1::ServerSocket1ClientConnect(TObject *Sender,
          TCustomWinSocket *Socket)
    {
    
    	   Socket->Data = new ClientSession;
    	   ClientSession *asession = (ClientSession*)Socket->Data;
    	   asession->SocketHandle = Socket->SocketHandle;
    
    }
    

    OnClientRead:

    void __fastcall TForm1::ServerSocket1ClientRead(TObject *Sender,
    	  TCustomWinSocket *Socket)
    {
    
    	ClientSession *asession = (ClientSession*) Socket->Data;
    	std::vector<unsigned char> ReceiveBuffer(Socket->ReceiveLength(), 0 );
    
    	if( !ReceiveBuffer.empty() )
    	{
    		int Received = Socket->ReceiveBuf( &ReceiveBuffer[0], ReceiveBuffer.size());
    		if( Received > 0 )
    		{
    		  asession->Paketsammler.insert( asession->Paketsammler.end(), ReceiveBuffer.begin(),ReceiveBuffer.begin() + Received );
    		  ServerHandleReceivedPackets(Socket);
    
    		}
    	}
    
    }
    

    HandleReceivedPackets:

    void __fastcall TForm1::ServerHandleReceivedPackets(TCustomWinSocket *Socket)
    {
    
    	ClientSession *asession = (ClientSession*) Socket->Data;
    
    	unsigned int SequenceId;
    
    	while(asession->Paketsammler.size() >= 8 )
    	{
    
    		 unsigned int TelegramLength = asession->Paketsammler[4] | asession->Paketsammler[5] << 8 |
    		 asession->Paketsammler[6] << 16 | asession->Paketsammler[7] << 24;
    
    		if( asession->Paketsammler.size() < TelegramLength )
    		return;
    
    		SequenceId = asession->Paketsammler[0] | asession->Paketsammler[1] << 8 | asession->Paketsammler[2] << 16
    		| asession->Paketsammler[3] << 24;
    		SequenceId = SequenceId & 0x3fffffff;
    
    		 Memo4->Lines->Add("Handle: " + String(asession->SocketHandle));
    		 Memo4->Lines->Add("ID: " + String(SequenceId));
    
    ...
    ......
    .............
    
                   asession->Paketsammler.erase( asession->Paketsammler.begin(), asession->Paketsammler.begin() + TelegramLength );
    

    OnClientDisconnect:

    void __fastcall TForm1::ServerSocket1ClientDisconnect(TObject *Sender,
    	  TCustomWinSocket *Socket)
    {
    
    	  delete (ClientSession*) Socket->Data;
    
    }
    

    Wie findet ihr diese Lösung? Funktionieren tuts bisher 🙂



  • Ich finde das "Ankleben" der ClientSession Objekte an TCustomWinSocket::Data nicht so schön. Ich würde das über eine std::map<TCustomWinSocket*, ClientSession> lösen, das hat dann noch den Vorteil, dass man keine dynamische Speicherverwaltung benötigt und ggf. über alle Session Objekte iterieren kann, falls man das irgendwann mal braucht (z.B. für Broadcasts).
    Ich hatte mal ein ähnliches Problem, bei sehr viele Telegramm gesendet worden sind, da hat sich die Speicherverwaltung des Vektors unangenehm bemerkbar gemacht. Wenn sehr viele kurze Telegramme eintreffen will der Vektor einen großen zusammenhängenden Speicher haben Für nur einen Client ist das vernachlässigbar, aber bei vielen Clients kann das ein Thema werden. Das zweite Problem ist wieder die Speicherverwaltung des Vektors. Angenommen, es haben sich einige Megabyte Daten im Puffer angesammelt, aber die einzelnen Telegramme sind relativ klein. Wenn ein Telegramm jetzt vom Anfang des Puffers entfernt wird, müssen alle folgenden Daten an den Anfang des Vektors verschoben werden, das kostet Zeit. Du solltest pro ServerHandleReceivedPackets Einsprung nur einen erase Aufruf auf dem Vektor ausführen, um möglichst selten Speicher verschieben zu müssen. Also nicht nach jedem behandelten Telegramm das Telegramm aus dem Puffer entfernen, sondern am Ende der Methode einen einzigen erase Aufruf, der alle Daten entfernt, die gerade behandelt worden sind.
    Warum die Klasse TForm1 friend der Klasse ClientSession sein muss ist mir schleierhaft, verpass´ der Klasse ClientSession ein vernünftiges Interface, dann brauchst du diese friend-Beziehung nicht.
    Möglicherweise solltest du eigene Server- und Clientklassen bauen, die die ganze Speicherverwaltung intern erledigen, dann kannst du das immer wieder benutzen.
    Ansonsten ist die Idee natürlich richtig, jeder Socket einen eigenen Empfangspuffer zu spendieren.



  • Supi, ich danke dir.
    std::map hatte ich mir gestern Abend schon mal angeschaut und hat natürlich seine klaren Vorteile. Der Löschvorgang auf dem Vector findet ja auch nur statt wenn ein vollständiges Telegram empfangen wurde, ist es unvollständig springe ich ja mit return aus der Schleife aus und warte bis das nächste kommt. Alle Telegramme bewegen sich im kleineren Kb Bereich.

    Natürlich fehlt in der Klasse noch ein Konstruktor und Destruktor, implementiere ich heute Nacht. Gerade der Destruktor ist wichtig um einen cleanup durchzuführen falls man doch ohne std::map weiter macht. Die friend class Beziehung habe ich eher nur Testweiße eingebaut und stellt auch keinen Anspruch auf Richtigkeit in dem frühen Stadium. Deine Ratschläge überdenke ich und schaue mal wie ich das ganze daraufhin optimieren kann.

    Der Hintergrund des ganzen ist ein Serverlayer für den Client, den ich bereits vor einem Jahr angefangen habe. Der Server soll später die Rechteverwaltung und User Accounts bereitstellen und die synchronisation aller Clients ohne zu pollen. Falls es jemanden interessiert: www.david-seay.de/yarcc



  • Die ClientSession Instanz zu Socket Verknüpfung mit einer std::map zu lösen bietet wirklich nur Vorteile. So mache ich das auch. Weiterer Punkt ist, dass die Klasse ClientSession auch noch einen
    Zeiger auf ein TClientSocket beherbergen soll, welches zur Laufzeit erstellt wird.

    Der Hintergrund ist ganz einfach so, dass jede am Serversocket bestehende Verbindung zusätzlich ein Clientsocket erhält um eine Art "Kontrollierter Tunnel" zum Hauptserver herzustellen.

    Habe ich dich richtig verstanden das ich dann keinen Destruktor für ClientSession benötige, da std::map erase/delete den cleanup der Klasse ClientSession übernehmen würde? Wenn ja müsste ich nur nach dem iterieren auf das erzeugte ClientSocket disconnecten und dann einfach die Verknüpfung aus der std::map löschen.



  • Das kann etwas heikel werden, wenn der Destruktor der ClientSession das TCustomWinSocket Objekt löscht. Grund dafür ist die Windows Message Queue, die Methode TForm1::ServerHandleReceivedPackets wird als Reaktion einer Nachricht in der Message Queue ausgeführt. Wenn du in dieser Funktion aber das Socket-Objekt löscht und nicht sicherstellen kannst, dass sich in der Message Queue keine Nachrichten mehr für diesen Socket befinden, kannst du damit auf die Nase fallen. Ich kenne mich mit den VCL Sockets allerdings nicht so gut aus, ich benutze die nicht mehr. Irgendwas funktionierte da immer nicht, deshalb benutze ich die nicht mehr. Was genau das war weiß ich nicht mehr, ich meine, da gingen irgendwo Events verloren.



  • Das neu instanzierte TClientSocket Objekt bekommt seine eigene Paketverarbeitung und hat mit den Serverpaketen erstmal nichts zu tun, aber ich weiss worauf du hinaus willst. Wenn ich das Socket mit dem Konstruktor erstelle und die Socketinformationen mit in die Klasse ablege, gibt es doch einen Weg diese Instanz sauber freizugeben. Wird das Socket vor freigabe disconnected fällt auch die Message Queue für die empfangenen Pakete weg da bis zum disconnect die Puffer abgearbeitet sind.

    Könntest du mir bitte mal ein Beispiel zeigen was du mit einem sauberen Klasseninterface meinst.



  • Zero01 schrieb:

    Das neu instanzierte TClientSocket Objekt bekommt seine eigene Paketverarbeitung und hat mit den Serverpaketen erstmal nichts zu tun, aber ich weiss worauf du hinaus willst. Wenn ich das Socket mit dem Konstruktor erstelle und die Socketinformationen mit in die Klasse ablege, gibt es doch einen Weg diese Instanz sauber freizugeben. Wird das Socket vor freigabe disconnected fällt auch die Message Queue für die empfangenen Pakete weg da bis zum disconnect die Puffer abgearbeitet sind.

    Mit Message Queue meine ich nicht die empfangenen Pakete, sondern die Windows Message Queue, die die Anwendung steuert. Und die fällt nicht mit dem Socket Objekt weg, sondern gehört dem Hauptthread der Anwendung.

    Zero01 schrieb:

    Könntest du mir bitte mal ein Beispiel zeigen was du mit einem sauberen Klasseninterface meinst.

    Ich weiß jetzt nicht, auf was du das jetzt beziehst. Auf jeden Fall sollte dein Formular nicht dafür verantwortlich sein, eingehende Daten zu bearbeiten und in Telegramme zu zerlegen.
    Ich habe das auf mehrere Schichten verteilt, die unterste Schicht ist nur dazu da, beliebige Daten zu versenden und zu empfangen. Die darüber liegende Schicht kümmert sich um das Protokoll und die Telegrammzerlegung. Diese Schicht ist beliebig austauschbar, je nach dem benutzten Protokoll. Darüber liegt dann eine Schicht, die die eingehenden Telegramme behandelt, das kann alles mögliche sein (Logger, GUI, etc).
    Die Kommunikation zwischen den Schichten finden immer durch das Observer Pattern statt, sodass die einzelnen Schichten nicht wissen, das die anderen Schichten mit den Daten anfangen.



  • Mal sehen ob ich verstanden habe was du meinst. Das Hauptformular soll sich nur um VCL relevante Operationen der Objekte kümmern. Demnach gehört ein neu initialisiertes TClientSocket nicht in die Klasse ClientSession sondern in die Hauptklasse die durch den Programmthread
    verwaltet wird. Wird ein Objekt im Hauptthread freigegeben, so wird dieses Objekt evenfalls von der Windows Message Queue "abgemeldet"

    ServerHandleReceivedPackets soll demnach auch keine Memberfunktion der Haupt VCL Klasse sein da dies auf einer anderen Ebene stattfindet. Eine strikte Trennung zwischen Objekten und Verarbeitungsroutinen um diese zu kapseln und für andere Projekte wiederverwertbar zu machen.

    Richtig?



  • Schwierig zu erklären, da müsste ich jetzt wirklich viel schreiben. Ich versuch´s mal mit ´ner Skizze:

    Binärdaten                 Telegramme
    Schicht 0  <------------>  Schicht 1  <------------>  Schicht 2
    

    Schicht 0 ist nur für die Datenübertragung verantwortlich und kennt weder Zweck noch Inhalt der Daten. Diese Schicht bietet Funktionen zum Schreiben von Daten und benutzt das Observer Pattern, um als Subject Änderungen zu veröffentlichen.

    Schicht 1 meldet sich als Observer an Schicht 0 an. Schicht 1 ist für die Umsetzung des Protokolls verantwortlich, d.h. sie baut aus den Telegrammen Binärdaten zusammen und übergibt sie an Schicht 0, die die Daten dann versendet.
    Außerdem benutzt sie ebenfalls das Observer Pattern, um als Subject die darüberliegende Schicht über den Empfang von Telegrammen zu informieren.
    Schicht 1 kümmert sich nur um das Senden und Empfangen von Telegrammen, ohne irgendwas mit ihnen anzustellen.

    Schicht 2 meldet sich wieder als Observer an Schicht 1 an und ist für die Behandlung und Interpretation der eingehenden Telegramme verantwortlich.
    Schicht 2 meldet sich als Observer an Schicht 1 an, um über eingehende Telegramme informiert zu werden.

    Implementieren kann man sowas z.B. per Komposition, die Implementation des Observer Patterns lasse ich mal außen vor.

    Pseudocode:

    //
    // Schicht 0
    //
    class TCPIPServer
    {
       TNotifyDataEvent  OnData_;   // Event Publisher, müssen implementiert werden
       TNotifyErrorEvent OnError_;
    
    public:
       void send( char* Data, unsigned int Length )
       {
          // Daten versenden
       }
    
       // dient als Event Handler für Socket Events und wird aufgerufen, wenn
       // Daten vom Socket gelesen werden können.
       void on_data( char* Data, unsigned int Length )
       {
          // Observer informieren
          OnData.fire( Data, Length );
       }
    
       void on_error( unsigned int ErrorCode )
       {
          // Observer informieren
          OnError.fire( ErrorCode );
       }
    };
    
    //
    // Schicht 1
    //
    class MyTelegramTCPIPServer
    {
       TCPIPServer           Server_;
       TNotifyTelegramEvent  OnData_;   // Event Publisher, müssen implementiert werden
       TNotifyErrorEvent    OnError_;
    
    public:
       MyTelegramTCPIPServer()
       {
          // als Observer an TCPIPServer anmelden
          Server_.OnError.subscribe( on_error );
          Server_.OnData.subscribe( on_data );
       }
    
       void send_telegram( const Telegram& T )
       {
          // Telegrammdaten zu Binärdaten umwandeln
          char* BinData = ...;
          unsigned int Length = ...;
          Server_.send( BinData, Length );
       }
    
       void on_data( const char* Data, unsigned int Length )
       {
          // Binärdaten wieder zu Telegrammen zusammensetzen und  
          // Observer benachrichtigen
          Telegram t = ...;
          OnTelegram.fire( t );
       }
    
       void on_error( unsigned int ErrorCode )
       {
          OnError.fire( ErrorCode );
       }
    };
    
    //
    // Schicht 2
    //
    class MyForm : public TForm
    {
    public:
       MyForm()
       {
          // als Observer an TCP/IP Server anmelden
          MyTelegramTCPIPServer.OnError.subscribe( on_error );
          MyTelegramTCPIPServer.OnTelegram.subscribe( on_telegram );
       }
    
       ~MyForm()
       {
          // Observer an TCP/IP Server abmelden
          MyTelegramTCPIPServer.OnError.unsubscribe( on_error );
          MyTelegramTCPIPServer.OnTelegram.unsubscribe( on_telegram );
       }
    
       void on_telegram( const Telegram& T ) 
       {
          // mach was mit T, z.B. in Memo eintragen, etc.
       }
    
       void on_error( unsigned int ErrorCode )
       {
          // informiere den Benutzer über einen Fehler
       }
    
       void OnClickButton()
       {
          Telegram t = ...;
          MyTelegramTCPIPServer.send( t );
        }
    };
    

    Das ist nur zur Übersicht gedacht und bei Weitem nicht vollständig. Ich hoffe aber, dass es dir weiterhilft.

    Edit:
    Flüchtigkeitsfehler korrigiert



  • Das nenne ich mal sauber gekapselt. Das dachte ich mir das du in etwa so vorgehst nach dem vorhergegangenen Post. Der Vorteil ist klar ersichtlich, da man die Schichten immer wieder verwenden kann und muss für spätere Projekte nur minimale Anpassungen vornehmen, wenn überhaupt. Die Daten verbleiben in jeder Schicht, eine Message geht raus das Daten vorhanden sind und die überliegende Schicht holt sie sich ab, verarbeitet sie und meldet wiederum an die untere Schicht, dass sie abgearbeitet sind - Schicht säubert die Puffer oder ggf. genutzte Objekte der Instanz und weiter gehts... Ich danke dir für das Beispiel und bietet mir noch mehr worüber ich mir Gedanken mache 🙂

    Gestern Nacht habe ich mal ein Quick and Dirty Verfahren geschrieben um zu sehen ob meine Idee überhaupt funktioniert. Mir ist es gelungen die Clientverbindung zum Master Server über den implementierten Serverlayer vollständig als gekapselte Instanz zu tunneln. Bis auf ein kleines Timing Problem bei der Initialisierung der Clientverbindung als Tunnel zwischen Serverlayer und Masterserver läuft das sehr gut. Das Timing Problem ist nebensächlich da der Authentifizierungshandshake noch nicht so programmiert ist wie er final sein soll - dann ist das Timing Problem nicht mehr vorhanden.

    Deinen Einwand im Bezug zur Löschung eines Objektes und der Message Queue habe ich letzte Nacht mal auf die Spitze getrieben um zu sehen wie sich das Programm im Fall der Fälle verhalten würde. Ein Konstruktor initialisiert ein Socket, weist die Notifier zu und öffnet die Verbindung. Der Destruktor schliesst falls eine Verbindung besteht und gibt das Socket wieder frei. Alle Notifier (Onblablabla) waren public Klassenmember sowie die Paketverarbeitungsroutinen und Puffer private. Alles innerhalb einer Klasse. Dann habe ich mehrere Clients verbunden und angefangen Pakete hin und her zu ballern. Wärend dessen habe ich einfach Clients disconnected (löst den Destruktor aus) und der Runtime Debugger/Codegard haben nicht einmal gemeckert. Es gab keinerlei Auffäligkeiten oder Exceptions.

    Nächster Schritt ist erstmal neben der Clientklasse noch eine Serverklasse zu bauen, wie schon von dir vorgeschlagen. Den Server kapsele ich so erstmal ganz aus dem Hauptprogramm aus, da mir meine bisherige Implementierung nachdem was wir besprochen haben ein wenig unsauber ausschaut.



  • Weiterer Ansatzpunkt:

    Die Klasse der Schicht 0 als Interface auslegen und als TCP/IP Server implementieren. Wenn das funktioniert kannst du weitere Schicht 0 Implementationen umsetzen, z.B. Kommunikation über Pipes oder die serielle Schnittstelle.



  • Die Socket Objekte zu Schichten habe ich erstmal aussen vor gelassen. Dies implementiere ich später. Das heisst zwar alles wieder auseinander klamüsern aber ich verliere sonst den Überblick ohne die vorhergehenden Sachen erstmal intus zu haben.

    Ich habe nun eine Server Klasse und eine Client Klasse und bin noch einen Schritt weiter gegangen ohne std::map Zuordnungen zwingend machen zu müssen, da die Client Klasse den ServerReadBuffer und den Pointer des erstellendem ServerSockets beinhaltet:

    Bei jeder Verbindung auf dem Server erstellt OnAccept eine Instanz von der ClientKlasse. Jeder Konstruktoraufruf der ClientKlasse initialisiert sein eigenes Socket innerhalb seiner Instanz und weist über

    Socket->Data = this
    

    den Pointer seiner eigenen Instanz zu (std::map kommt hier noch falls später iteriert werden muss). Die ClientKlasse speichert die SocketHandleId des ServerSockets, von dem sie erstellt wurde.

    Empfängt der Server nun ein Paket hole ich mir über Socket->Data den Zeiger auf die jeweilige ClientKlasse Instanz und fülle am ServerSocket den ServerReadbuffer und am Client den ClientReadbuffer. Da beide Buffer innerhalb einer Klasse liegen, kann man für den Tunnel die Pakete wesentlich einfacher zuordnen und über die Pointer die jeweiligen Sockets der Instanzen identifizieren. Nach einem längeren Testlauf kann ich nun sagen, dass dies hervorragend funktioniert.

    Edit: trotzdem grosser Fehler. Ich schreibe gerade das ganze um



  • Doc, du hattest vollkommen Recht. Ohne z.B std::map oder einer TObjectList stösst man bei diesem vorhaben schnell an seine Grenzen. Die Idee den Serversammler in die Clientklassen Instanz zu integrieren war ebenfalls nur unfug. Es funktionierte alles bis auf das Szenario wenn der "Master" serverseitig die Verbindung geschlossen wird oder ein Socketerror entsteht. Der Versuch die Verbindung vom Serverlayer aus zum eigentlichen Client zu beenden hat seinen Lesepuffer unter den Füssen bedingt durch den Destruktor weggezogen, woraufhin die Windows Message Queue eine Zugriffsverletzung auslöste. Da mein Vorhaben nur unter bestimmten Bedingungen versagt, habe ich gestern angefangen alles umzustrukturieren. Die Clientklasse initialisiert ihr Socket, stellt Ressourcen bereit. Die Serverklasse kümmert sich um die Ressourcen ihres Sockets. Die std::map Verknüpft als pair die Pointer beider Instanzen da diese eine Einheit bilden um den Tunnel zu realisieren. Findet in einer der beiden ein Socketerror oder Disconnect statt kommt die map ins Spiel um beide Instanzen auf einen Schlag zu säubern und freizugeben.

    Dazu 2 Fragen:
    std::map.erase löst die Destruktoren des pairs aus oder wie geht da erase vor?

    Warum kann eine Klasse kein Owner eines Sockets sein?

    std::map habe ich gestern Abend schon implementiert. Zunächst mit Pointer auf die Socketobjekte, was aber auch nicht korrekt war. Jeder Klasseninstanz ist der Pointer zu seinem Socket bereits bekannt, also unfug. Heute Abend wird die std::map jeweils 2 Zeiger auf die Klasseninstanzen als pair beherbergen. Das Serversocket binde ich wieder ins Hauptprogramm ein weil dies ohnehin nur einmalig ausgeführt wird. Beide Klassen baue ich vollständig in sich gekapselt wieder um so wie es sein muss.



  • Ich habe ein massives Problem mit den Destruktoren. Sobald am ServerSocket eine Clientverbindung geschlossen wird, wird der Destruktor der Klasse ClientSession wie in einer Endlosschleife ausgelöst bis das Programm vollständig abstürzt.
    Erkennt jemand von euch den Fehler?

    Edit: @DocShoe: Deine Anmerkungen sind keinesfalls in Vergessenheit geraten. Ich versuche zunächst erstmal die Grundfunktion herzustellen bevor ich deine Anmerkungen implementiere. Die std::map hatte ich hier erstmal wieder entfernt.

    //----------------------------------------------------------------------------------------
    //----------------------  Klassen
    //----------------------------------------------------------------------------------------
    
    class ServerSession
    {
     friend class ClientSession;
    
       public:
    	ServerSession(ClientSession *,TCustomWinSocket *);
    	~ServerSession();
    
       ClientSession *myclientsession;
       TCustomWinSocket *myserversocket;
       std::vector<unsigned char> ServerPaketsammler;
       void __fastcall ServerHandleReceivedPackets(TCustomWinSocket*);
    };
    
    class ClientSession
    {
    
    friend class ServerSession;
    
    public:
       ClientSession(TCustomWinSocket *);
       ~ClientSession();
    
       void __fastcall ClientSocket1Connect(TObject *Sender,
    	  TCustomWinSocket *Socket);
       void __fastcall ClientSocket1Read(TObject *Sender,
    	  TCustomWinSocket *Socket);
       void __fastcall ClientSocket1Disconnect(TObject *Sender,
    	  TCustomWinSocket *Socket);
       void __fastcall ClientSocket1Error(TObject *Sender,
    	  TCustomWinSocket *Socket, TErrorEvent ErrorEvent, int &ErrorCode);
    
       TClientSocket *SessionClient1;
       TCustomWinSocket *myserversocket;
       ServerSession *myserversession;
    
    private:
       void __fastcall ClientHandleReceivedPackets(TCustomWinSocket *);
       std::vector<unsigned char> ClientPaketsammler;
    
    };
    

    ClientSession

    //----------------------------------------
    //---Konstruktor der Klasse ClientSession
    //----------------------------------------
    ClientSession::ClientSession(TCustomWinSocket *serversocket)
     :myserversocket(serversocket)
    {
    
      Form1->Memo4->Lines->Add("Konstruktor ClientSession");
    
      SessionClient1 = new TClientSocket(NULL);
      SessionClient1->Socket->Data = this;
      SessionClient1->ClientType = ctNonBlocking;
      SessionClient1->Host = "90.123.44.5";
      SessionClient1->Port = 25505;
      SessionClient1->OnConnect = ClientSocket1Connect;
      SessionClient1->OnDisconnect = ClientSocket1Disconnect;
      SessionClient1->OnError = ClientSocket1Error;
      SessionClient1->OnRead = ClientSocket1Read;
      SessionClient1->Open();
    
    }
    //----------------------------------------
    //--- Destruktor der Klasse ClientSession
    //----------------------------------------
    ClientSession::~ClientSession()
    {
    
      Form1->Memo4->Lines->Add("Destruktor ClientSession");
    
      this->SessionClient1->Socket->Close();
      this->myserversocket->Close();
    
      delete this->SessionClient1;
    
    }
    //--------------------------------------------------------------
    
    //---------------------------------------------------------------------------
    //-------  CLIENT
    //---------------------------------------------------------------------------
    
    void __fastcall ClientSession::ClientSocket1Connect(TObject *Sender,
    	  TCustomWinSocket *Socket)
    {
    
       Form1->Memo4->Lines->Add(Time().TimeString() + " [ClientSession] Connected");
    
    }
    //---------------------------------------------------------------------------
    
    void __fastcall ClientSession::ClientSocket1Read(TObject *Sender,
          TCustomWinSocket *Socket)
    {
    
    	 ClientSession *asession = (ClientSession*) Socket->Data;
    	 std::vector<unsigned char> ReceiveBuffer(Socket->ReceiveLength(),0);
    
    	if(!ReceiveBuffer.empty())
    	{
    		int Received = Socket->ReceiveBuf( &ReceiveBuffer[0], ReceiveBuffer.size() );
    		if( Received > 0 )
    		{
    			asession->ClientPaketsammler.insert( asession->ClientPaketsammler.end(), ReceiveBuffer.begin(), ReceiveBuffer.begin() + Received );
    			ClientHandleReceivedPackets(Socket);
    		}
    	}
    
    }
    //---------------------------------------------------------------------------
    void __fastcall ClientSession::ClientHandleReceivedPackets(TCustomWinSocket *Socket)
    {
    
    	ClientSession *asession = (ClientSession*) Socket->Data;
    	unsigned int SequenceId;
    
    	while(asession->ClientPaketsammler.size() >= 8 )
    	{
    
    		 unsigned int TelegramLength = asession->ClientPaketsammler[4] | asession->ClientPaketsammler[5] << 8 | asession->ClientPaketsammler[6] << 16 | asession->ClientPaketsammler[7] << 24;
    
    		if( asession->ClientPaketsammler.size() < TelegramLength )
    		return;
    
    		SequenceId = asession->ClientPaketsammler[0] | asession->ClientPaketsammler[1] << 8 | asession->ClientPaketsammler[2] << 16 | asession->ClientPaketsammler[3] << 24;
    		SequenceId = SequenceId & 0x3fffffff;
    
    		std::vector<unsigned char> Paket(asession->ClientPaketsammler.begin(), asession->ClientPaketsammler.begin() + TelegramLength);
    
    		//Vom Master Server kommenden an den entsprechenden Client weiterleiten
    		asession->myserversocket->SendBuf(&Paket[0],Paket.size());
    
    		asession->ClientPaketsammler.erase( asession->ClientPaketsammler.begin(), asession->ClientPaketsammler.begin() + TelegramLength );
    
    	}
    
    }
    //------------------------------------------------------------------------------------------------------
    
    void __fastcall ClientSession::ClientSocket1Disconnect(TObject *Sender,
    	  TCustomWinSocket *Socket)
    {
    
    	 delete (ClientSession*) Socket->Data;
    	 Form1->Memo4->Lines->Add(Time().TimeString() +  " [ClientSession] Disconnected");
    
    }
    //---------------------------------------------------------------------------
    void __fastcall ClientSession::ClientSocket1Error(TObject *Sender,
    	  TCustomWinSocket *Socket, TErrorEvent ErrorEvent, int &ErrorCode)
    {
    
    	delete (ClientSession*) Socket->Data;
    	Form1->Memo4->Lines->Add(Time().TimeString() + " [ClientSession] Socket Error: " + String(ErrorCode)) ;
    	ErrorCode = 0;
    
    }
    //--------------------------------------------------------------------------
    

    ServerSession:

    //---------------------------------------------------------------------------
    //-----------   SERVER
    //---------------------------------------------------------------------------
    void __fastcall TForm1::Button14Click(TObject *Sender)
    {
    	if(Button14->Caption == "Start")
    	{
    	   Button14->Caption = "Stop";
    
    	   ServerSocket1 = new TServerSocket(Form1);
    	   ServerSocket1->ServerType = stNonBlocking;
    	   ServerSocket1->OnClientConnect = ServerSocket1Connect;
    	   ServerSocket1->OnClientRead = ServerSocket1ClientRead;
    	   ServerSocket1->OnClientDisconnect = ServerSocket1ClientDisconnect;
    	   ServerSocket1->OnClientError = ServerSocket1ClientError;
    	   ServerSocket1->Port = 14888;
    	   ServerSocket1->Active = true;
    	}
    	else
    	{
    
    	  //--- Alle verbundenen Clients trennen
    	  if(ServerSocket1->Socket->ActiveConnections > 0)
    	  {
    		 for(int i = 0; i < ServerSocket1->Socket->ActiveConnections; i++)
    		{
    		  ServerSocket1->Socket->Disconnect(i);
    		}
    	  }
    
    	   ServerSocket1->Active = false;
    	   delete ServerSocket1;
    	   ServerSocket1 = NULL;
    	   Button14->Caption = "Start";
    	}
    }
    //------------------------------------------------------------------------------
    
    //--- Konstruktor der Klasse ServerSession
    ServerSession::ServerSession(ClientSession *clientsession,TCustomWinSocket *ServerSocket)
       :myclientsession(clientsession),myserversocket(ServerSocket)
    {
    
       Form1->Memo4->Lines->Add("Konstruktor ServerSession");
    
    }
    //------------------------
    //--- Destruktor der Klasse ServerSession
    //------------------------
    ServerSession::~ServerSession()
    {
    
    	Form1->Memo4->Lines->Add("Destruktor ServerSession");
    	this->myclientsession->SessionClient1->Socket->Close();
    	this->myserversocket->Close();
    
    }
    //------------------------------------------------------------------------------
    void __fastcall TForm1::ServerSocket1Connect(TObject *Sender,
    	  TCustomWinSocket *Socket)
    {
    
    	   ClientSession *asession = new ClientSession(Socket);
    	   Socket->Data = new ServerSession(asession,Socket);
    	   asession->myserversession = (ServerSession*) Socket->Data;
    
    	   Form1->Memo4->Lines->Add(Time().TimeString() + " [YARCC Client] Connected");
    
    }
    
    //------------------------------------------------------------------------------
    void __fastcall TForm1::ServerSocket1ClientRead(TObject *Sender,
    	  TCustomWinSocket *Socket)
    {
    
    	ServerSession *asession = (ServerSession *)Socket->Data;
    
    	std::vector<unsigned char> ReceiveBuffer(Socket->ReceiveLength(), 0 );
    
    	if( !ReceiveBuffer.empty() )
    	{
    		int Received = Socket->ReceiveBuf( &ReceiveBuffer[0], ReceiveBuffer.size());
    		if(Received > 0)
    		{
    		  asession->ServerPaketsammler.insert( asession->ServerPaketsammler.end(), ReceiveBuffer.begin(),ReceiveBuffer.begin() + Received );
    		  asession->ServerHandleReceivedPackets(Socket);
    		}
    	}
    
    }
    //------------------------------------------------------------------------------
    void __fastcall ServerSession::ServerHandleReceivedPackets(TCustomWinSocket *Socket)
    {
    
    	ServerSession *asession = (ServerSession*)Socket->Data;
    
    	unsigned int SequenceId;
    	while(asession->ServerPaketsammler.size() >= 8 )
    	{
    
    		 unsigned int TelegramLength = asession->ServerPaketsammler[4] | asession->ServerPaketsammler[5] << 8 |
    		 asession->ServerPaketsammler[6] << 16 | asession->ServerPaketsammler[7] << 24;
    
    		if( asession->ServerPaketsammler.size() < TelegramLength )
    		return;
    
    		SequenceId = asession->ServerPaketsammler[0] | asession->ServerPaketsammler[1] << 8 | asession->ServerPaketsammler[2] << 16
    		| asession->ServerPaketsammler[3] << 24;
    		SequenceId = SequenceId & 0x3fffffff;
    
    		std::vector<unsigned char>Paket(asession->ServerPaketsammler.begin(), asession->ServerPaketsammler.begin() + TelegramLength );
    
    		//******** Packet vom Client kommend an Master Server weiterleiten
    		asession->myclientsession->SessionClient1->Socket->SendBuf(&Paket[0],Paket.size());
    
    		asession->ServerPaketsammler.erase( asession->ServerPaketsammler.begin(), asession->ServerPaketsammler.begin() + TelegramLength );
    	}
    
    }
    //------------------------------------------------------------------------------
    void __fastcall TForm1::ServerSocket1ClientDisconnect(TObject *Sender,
    	  TCustomWinSocket *Socket)
    {
    
    	  delete (ServerSession *)Socket->Data;
    	  Form1->Memo4->Lines->Add(Time().TimeString() + " [YARCC Client] Disconnected");
    
    }
    //------------------------------------------------------------------------------
    void __fastcall TForm1::ServerSocket1ClientError(TObject *Sender,
    	  TCustomWinSocket *Socket, TErrorEvent ErrorEvent, int &ErrorCode)
    {
    
    		delete (ServerSession *)Socket->Data;
    		Form1->Memo4->Lines->Add(Time().TimeString() + " [YARCC Client] Socket Error: " + String(ErrorCode)) ;
    
    		ErrorCode = 0;
    
    }
    //------------------------------------------------------------------------------
    


  • Kann die zuvor genannte Problematik dadurch entstehen das der Aufruf delete ASession auch die in der Klasse enthaltenen Objekte in Form von Pointern mit zerstört?
    Eigentlich sollten doch nur die Pointer zerstört werden und nicht die Objekte auf die der Pointer zeigt.



  • Guck dir mal die Adresse des TCustomWinSocket Objekts an, ist es immer das gleiche Objekt, das zerstört wird?

    Außerdem sieht dein Design seltsam aus:
    - warum besitzt ServerSession einen Zeiger auf ein ClientSession ?
    - warum sind beide friends voneinander?
    - warum verwaltest du den Speicher für ClientSession manuell?



  • Moin Doc,
    Der Grund das jede Klasse einen Pointer auf die andere besitzt ist, dass jeweils eine Instanz ClientSession zu einer Instanz ServerSession eine logische Einheit bildet um die Tunneling Funktion bereitzustellen (später kommt noch eine Berechtigungsfunktion bzw. Usermanagment mit rein um Pakete die nicht zur Berechtigungsgruppe gehören verworfen werden - jetzt nicht wichtig da die Klassen und Funktionen schon 2006 für LFS Dedilink von mir entwickelt wurden aber papplapapp).

    Sobald eine Klasseninstanz zerstört wird soll bekannt seine welche Instanz mit ihr ein Paar bildet um diese ebenfalls zu killen. Daher die gegenseitige Pointerzuweisung. Die friendbeziehungen erleichtern mir den Zugriff auf die Member der anderen Klasse



  • Aaargh, sich mal 2 Tage den Kopf frei machen und vom Projekt zu entfernen wirkt manchmal Wunder. Ist doch klar das die Destruktoren ärger machen. Wenn ich im OnSocketError und OnDisconnect Event ein delete einer Session Klasse aufrufe und im Destruktor der Klasse sich ein Socket->Disconnect Aufruf befindet, ruft sich bei einem Socket Error der Destruktor wie in einer Endlosschleife selbst auf bis das Programm abstürzt. Man bin ich blind...
    Jetzt muss ich zusehen wie ich das am besten löse

    Edit: Jetzt funktionierts! Habt ihr evtl. Optimierungsvorschläge für mich?

    //----------------------------------------
    //---Konstruktor der Klasse ClientSession
    //----------------------------------------
    ClientSession::ClientSession(TCustomWinSocket *serversocket)
     :myserversocket(serversocket)
    {
    
      Form1->Memo4->Lines->Add("Konstruktor ClientSession");
    
      SessionClient1 = new TClientSocket(NULL);
      SessionClient1->Socket->Data = this;
      SessionClient1->ClientType = ctNonBlocking;
      SessionClient1->Host = "90.123.45.5";
      SessionClient1->Port = 25505;
      SessionClient1->OnConnect = ClientSocket1Connect;
      SessionClient1->OnDisconnect = ClientSocket1Disconnect;
      SessionClient1->OnError = ClientSocket1Error;
      SessionClient1->OnRead = ClientSocket1Read;
      SessionClient1->Open();
    
    }
    //----------------------------------------
    //--- Destruktor der Klasse ClientSession
    //----------------------------------------
    ClientSession::~ClientSession()
    {
    
      Form1->Memo4->Lines->Add("Destruktor ClientSession");
    
      delete this->SessionClient1;
      this->SessionClient1 = NULL;
    
    }
    //--------------------------------------------------------------
    
    //---------------------------------------------------------------------------
    //-------  CLIENT
    //---------------------------------------------------------------------------
    
    void __fastcall ClientSession::ClientSocket1Connect(TObject *Sender,
    	  TCustomWinSocket *Socket)
    {
    
       Form1->Memo4->Lines->Add(Time().TimeString() + " [ClientSession] Connected");
    
    }
    //---------------------------------------------------------------------------
    
    void __fastcall ClientSession::ClientSocket1Read(TObject *Sender,
          TCustomWinSocket *Socket)
    {
    
    	 ClientSession *asession = (ClientSession*) Socket->Data;
    	 std::vector<unsigned char> ReceiveBuffer(Socket->ReceiveLength(),0);
    
    	if(!ReceiveBuffer.empty())
    	{
    		int Received = Socket->ReceiveBuf( &ReceiveBuffer[0], ReceiveBuffer.size() );
    		if( Received > 0 )
    		{
    			asession->ClientPaketsammler.insert( asession->ClientPaketsammler.end(), ReceiveBuffer.begin(), ReceiveBuffer.begin() + Received );
    			ClientHandleReceivedPackets(Socket);
    		}
    	}
    
    }
    //---------------------------------------------------------------------------
    void __fastcall ClientSession::ClientHandleReceivedPackets(TCustomWinSocket *Socket)
    {
    
    	ClientSession *asession = (ClientSession*) Socket->Data;
    	unsigned int SequenceId;
    
    	while(asession->ClientPaketsammler.size() >= 8 )
    	{
    
    		 unsigned int TelegramLength = asession->ClientPaketsammler[4] | asession->ClientPaketsammler[5] << 8 | asession->ClientPaketsammler[6] << 16 | asession->ClientPaketsammler[7] << 24;
    
    		if( asession->ClientPaketsammler.size() < TelegramLength )
    		return;
    
    		SequenceId = asession->ClientPaketsammler[0] | asession->ClientPaketsammler[1] << 8 | asession->ClientPaketsammler[2] << 16 | asession->ClientPaketsammler[3] << 24;
    		SequenceId = SequenceId & 0x3fffffff;
    
    		std::vector<unsigned char> Paket(asession->ClientPaketsammler.begin(), asession->ClientPaketsammler.begin() + TelegramLength);
    
    		//Vom Master Server kommenden an den entsprechenden Client weiterleiten
    		asession->myserversocket->SendBuf(&Paket[0],Paket.size());
    
    		asession->ClientPaketsammler.erase( asession->ClientPaketsammler.begin(), asession->ClientPaketsammler.begin() + TelegramLength );
    
    	}
    
    }
    //------------------------------------------------------------------------------------------------------
    
    void __fastcall ClientSession::ClientSocket1Disconnect(TObject *Sender,
    	  TCustomWinSocket *Socket)
    {
    	  if(this->SessionClient1->Socket->Data != NULL)
    	{
    	 ClientSession *asession = (ClientSession*) Socket->Data;
    	 asession->myserversession->myserversocket->Data = NULL;
    	 asession->myserversocket->Close();
    	 delete asession->myserversession;
    	 delete asession;
    
    	 asession->myserversession = NULL;
    	 asession = NULL;
    	}
    
    	 Form1->Memo4->Lines->Add(Time().TimeString() +  " [ClientSession] Disconnected");
    
    }
    //---------------------------------------------------------------------------
    void __fastcall ClientSession::ClientSocket1Error(TObject *Sender,
    	  TCustomWinSocket *Socket, TErrorEvent ErrorEvent, int &ErrorCode)
    {
    
    	ClientSession *asession = (ClientSession*) Socket->Data;
    	asession->SessionClient1->Socket->Close();
    
    	Form1->Memo4->Lines->Add(Time().TimeString() + " [ClientSession] Socket Error: " + String(ErrorCode)) ;
    	ErrorCode = 0;
    
    }
    //--------------------------------------------------------------------------
    
    //---------------------------------------------------------------------------
    //-----------   SERVER
    //---------------------------------------------------------------------------
    void __fastcall TForm1::Button14Click(TObject *Sender)
    {
    	if(Button14->Caption == "Start")
    	{
    	   Button14->Caption = "Stop";
    
    	   ServerSocket1 = new TServerSocket(Form1);
    	   ServerSocket1->ServerType = stNonBlocking;
    	   ServerSocket1->OnClientConnect = ServerSocket1Connect;
    	   ServerSocket1->OnClientRead = ServerSocket1ClientRead;
    	   ServerSocket1->OnClientDisconnect = ServerSocket1ClientDisconnect;
    	   ServerSocket1->OnClientError = ServerSocket1ClientError;
    	   ServerSocket1->Port = 14888;
    	   ServerSocket1->Active = true;
    	}
    	else
    	{
    
    	  //--- Alle verbundenen Clients trennen
    	  if(ServerSocket1->Socket->ActiveConnections > 0)
    	  {
    		 for(int i = 0; i < ServerSocket1->Socket->ActiveConnections; i++)
    		{
    		  ServerSocket1->Socket->Disconnect(i);
    		}
    	  }
    
    	   ServerSocket1->Active = false;
    	   delete ServerSocket1;
    	   ServerSocket1 = NULL;
    	   Button14->Caption = "Start";
    	}
    }
    //------------------------------------------------------------------------------
    
    //--- Konstruktor der Klasse ServerSession
    ServerSession::ServerSession(ClientSession *clientsession,TCustomWinSocket *ServerSocket)
       :myclientsession(clientsession),myserversocket(ServerSocket)
    {
    
       Form1->Memo4->Lines->Add("Konstruktor ServerSession");
    
    }
    //------------------------
    //--- Destruktor der Klasse ServerSession
    //------------------------
    ServerSession::~ServerSession()
    {
    
    	Form1->Memo4->Lines->Add("Destruktor ServerSession");
    
    }
    //----------------------------------------------------------------------------
    void __fastcall TForm1::ServerSocket1ClientRead(TObject *Sender,
    	  TCustomWinSocket *Socket)
    {
    
    	ServerSession *asession = (ServerSession *)Socket->Data;
    
    	std::vector<unsigned char> ReceiveBuffer(Socket->ReceiveLength(), 0 );
    
    	if( !ReceiveBuffer.empty() )
    	{
    		int Received = Socket->ReceiveBuf( &ReceiveBuffer[0], ReceiveBuffer.size());
    		if(Received > 0)
    		{
    		  asession->ServerPaketsammler.insert( asession->ServerPaketsammler.end(), ReceiveBuffer.begin(),ReceiveBuffer.begin() + Received );
    		  asession->ServerHandleReceivedPackets(Socket);
    		}
    	}
    
    }
    //---------------------------------------------------------------------------------------------------------
    void __fastcall ServerSession::ServerHandleReceivedPackets(TCustomWinSocket *Socket)
    {
    
    	ServerSession *asession = (ServerSession*)Socket->Data;
    
    	unsigned int SequenceId;
    	while(asession->ServerPaketsammler.size() >= 8 )
    	{
    
    		 unsigned int TelegramLength = asession->ServerPaketsammler[4] | asession->ServerPaketsammler[5] << 8 |
    		 asession->ServerPaketsammler[6] << 16 | asession->ServerPaketsammler[7] << 24;
    
    		if( asession->ServerPaketsammler.size() < TelegramLength )
    		return;
    
    		SequenceId = asession->ServerPaketsammler[0] | asession->ServerPaketsammler[1] << 8 | asession->ServerPaketsammler[2] << 16
    		| asession->ServerPaketsammler[3] << 24;
    		SequenceId = SequenceId & 0x3fffffff;
    
    		/*--- Verzögerungstest TEST!!!!
    		if(SequenceId == 1)
    		{
    			for(int i = 0; i < 10; i++)
    			{
    			  Sleep(10);
    			  Application->ProcessMessages();
    
    			}
    		}
    		*/
    
    		std::vector<unsigned char>Paket(asession->ServerPaketsammler.begin(), asession->ServerPaketsammler.begin() + TelegramLength );
    
    		//******** Packet vom Client kommend an Master Server weiterleiten
    		asession->myclientsession->SessionClient1->Socket->SendBuf(&Paket[0],Paket.size());
    
    		asession->ServerPaketsammler.erase( asession->ServerPaketsammler.begin(), asession->ServerPaketsammler.begin() + TelegramLength );
    	}
    
    }
    //----------------------------------------------------------------------------------------------------------
    void __fastcall TForm1::ServerSocket1Connect(TObject *Sender,
    	  TCustomWinSocket *Socket)
    {
    
    	   ClientSession *asession = new ClientSession(Socket);
    	   Socket->Data = new ServerSession(asession,Socket);
    	   asession->myserversession = (ServerSession*) Socket->Data;
    
    	   Form1->Memo4->Lines->Add(Time().TimeString() + " [YARCC Client] Connected");
    
    }
    
    //--------------------------------------------------------------------------------------------------------
    void __fastcall TForm1::ServerSocket1ClientDisconnect(TObject *Sender,
    	  TCustomWinSocket *Socket)
    {
    
    	  if((ServerSession*)Socket->Data != NULL)
    	 {
    	  ServerSession *asession = (ServerSession*)Socket->Data;
    	  asession->myclientsession->SessionClient1->Socket->Data = NULL;
    	  asession->myclientsession->SessionClient1->Socket->Close();
    
    	  delete asession->myclientsession;
    	  delete asession;
    
    	  asession->myclientsession = NULL;
    	  asession = NULL;
    	 }
    
    	  Form1->Memo4->Lines->Add(Time().TimeString() + " [YARCC Client] Disconnected");
    
    }
    //--------------------------------------------------------------------------------------------------------
    void __fastcall TForm1::ServerSocket1ClientError(TObject *Sender,
    	  TCustomWinSocket *Socket, TErrorEvent ErrorEvent, int &ErrorCode)
    {
    
    	  ServerSession *asession = (ServerSession*)Socket->Data;
    	  asession->myserversocket->Close();
    
    	  Form1->Memo4->Lines->Add(Time().TimeString() + " [YARCC Client] Socket Error: " + String(ErrorCode)) ;
    
    	  ErrorCode = 0;
    
    }
    //--------------------------------------------------------------------------------------------------------
    


  • Leider schlägt mein Code immernoch fehl wenn der Server zu dem ClientSession eine Verbindung aufbaut, die Verbindung trennt bzw: ein Socket Error eintritt. Alle anderen möglichen Szenarios funktionieren ohne Speicherlecks oder Fehlermeldungen.

    Ich habe nun den Lösungsweg über eine Klasseninstanz bestritten, da in der älteren Version beide Instanzen ein Paar bildeten, gleichzeitig Leben und Sterben. Also warum nicht das Vorhaben innerhalb einer Klasseninstanz realisieren.

    Der Fehler: Wird das OnDisconnect Event von myclientsocket ausgelöst, verursacht der darin enthaltene Code folgende Exception - Socket Error 10038, ein Vorgang bezog sich auf ein Objekt was kein Socket ist, bei Api "closesocket" aufgetreten. Danach kommt eine Exception Read of Adress 0xefefefef. Ich verfahre genauso wie bei einem Disconnect am Serversocket wo es keinerlei Auffälligkeiten gibt. Erkennt jemand von euch wo der Fehler liegt?

    class YarccSession
    {
    
    //friend class TForm1;
    
    public:
       YarccSession(TCustomWinSocket *);
       ~YarccSession();
    
       void __fastcall ClientSocket1Connect(TObject *Sender,
    	  TCustomWinSocket *Socket);
       void __fastcall ClientSocket1Read(TObject *Sender,
    	  TCustomWinSocket *Socket);
       void __fastcall ClientSocket1Disconnect(TObject *Sender,
    	  TCustomWinSocket *Socket);
       void __fastcall ClientSocket1Error(TObject *Sender,
    	  TCustomWinSocket *Socket, TErrorEvent ErrorEvent, int &ErrorCode);
    
       TClientSocket *myclientsocket;
       TCustomWinSocket *myserversocket;
    
    private:
       void __fastcall ClientHandleReceivedPackets(TCustomWinSocket *);
       void __fastcall ServerHandleReceivedPackets(TCustomWinSocket*);
       std::vector<unsigned char> ClientPaketsammler;
       std::vector<unsigned char> ServerPaketsammler;
    
    };
    
    //----------------------------------------
    YarccSession::YarccSession(TCustomWinSocket *serversocket)
     :myserversocket(serversocket)
    {
    
      Form1->Memo4->Lines->Add("Konstruktor YarccSession");
    
      myserversocket->Data = this;
      myclientsocket = new TClientSocket(NULL);
      myclientsocket->Socket->Data = this;
      myclientsocket->ClientType = ctNonBlocking;
      myclientsocket->Host = "90.197.152.229";
      myclientsocket->Port = 25505;
      myclientsocket->OnConnect = ClientSocket1Connect;
      myclientsocket->OnDisconnect = ClientSocket1Disconnect;
      myclientsocket->OnError = ClientSocket1Error;
      myclientsocket->OnRead = ClientSocket1Read;
      myclientsocket->Open();
    
    }
    //----------------------------------------
    //--- Destruktor der Klasse ClientSession
    //----------------------------------------
    YarccSession::~YarccSession()
    {
       Form1->Memo4->Lines->Add("Destruktor YarccSession");
    
       delete this->myclientsocket;
       this->myclientsocket = NULL;
    
    }
    //--------------------------------------------------------------
    

    Hier tritt der Fehler ein:

    void __fastcall YarccSession::ClientSocket1Disconnect(TObject *Sender,
    	  TCustomWinSocket *Socket)
    {
    	  if(Socket->Data != NULL)
    	{
    		YarccSession *asession = (YarccSession*)Socket->Data;
    		asession->myclientsocket->Socket->Data = NULL;
    		asession->myserversocket->Data = NULL;
    
    		if(asession->myserversocket->Connected)
    		asession->myserversocket->Close();
    
    		delete asession;
    		asession = NULL;
    	}
    
    	 Form1->Memo4->Lines->Add(Time().TimeString() +  " [ClientSession] Disconnected");
    
    }
    //---------------------------------------------------------------------------
    void __fastcall YarccSession::ClientSocket1Error(TObject *Sender,
    	  TCustomWinSocket *Socket, TErrorEvent ErrorEvent, int &ErrorCode)
    {
    
    	 YarccSession *asession = (YarccSession*) Socket->Data;
    
    	 if(asession->myclientsocket->Socket->Connected)
    	 asession->myclientsocket->Socket->Close();
    
    	 Form1->Memo4->Lines->Add(Time().TimeString() + " [ClientSession] Socket Error: " + String(ErrorCode)) ;
    	 ErrorCode = 0;
    
    }
    
    void __fastcall TForm1::ServerSocket1Connect(TObject *Sender,
    	  TCustomWinSocket *Socket)
    {
    
    	   new YarccSession(Socket);
    	   Form1->Memo4->Lines->Add(Time().TimeString() + " [YARCC Client] Connected");
    
    }
    
    //--------------------------------------------------------------------------------------------------------
    void __fastcall TForm1::ServerSocket1ClientDisconnect(TObject *Sender,
    	  TCustomWinSocket *Socket)
    {
    
    	  if(Socket->Data != NULL)
    	 {
    		 YarccSession *asession = (YarccSession*)Socket->Data;
    		 asession->myserversocket->Data = NULL;
    		 asession->myclientsocket->Socket->Data = NULL;
    
    		 if(asession->myclientsocket->Socket->Connected)
    		 asession->myclientsocket->Socket->Close();
    
    		 delete asession;
    		 asession = NULL;
    	 }
    
    	 Form1->Memo4->Lines->Add(Time().TimeString() + " [YARCC Client] Disconnected");
    
    }
    //--------------------------------------------------------------------------------------------------------
    void __fastcall TForm1::ServerSocket1ClientError(TObject *Sender,
    	  TCustomWinSocket *Socket, TErrorEvent ErrorEvent, int &ErrorCode)
    {
    
    	  YarccSession *asession = (YarccSession*)Socket->Data;
    
    	  if(asession->myserversocket->Connected)
    	  asession->myserversocket->Close();
    
    	  Form1->Memo4->Lines->Add(Time().TimeString() + " [YARCC Client] Socket Error: " + String(ErrorCode)) ;
    
    	  ErrorCode = 0;
    
    }
    //--------------------------------------------------------------------------------------------------------
    

Log in to reply