[de] Client Trennung mit Dispose verursacht Absturz (gelöst)

Topics: Technical Support
Oct 29, 2011 at 9:37 AM

Guten Morgen,

zunächst einmal großes Lob an die Entwickler dieses Projekts. Endlich kann ich auch eine Anwendung schreiben, die im Netzwerk funktioniert ohne das ich direkt den Überblick verliere ;)

Trotzdem bin ich jetzt an ein Problem gestoßen, welches ich im Moment nicht weg bekomme.

-Ich habe eine Verbindung mit einer Server Console wie im MiniChat Beispiel.

-Ein Client verbindet dann mit diesem Server per Knopfdruck, was auch wunderbar funktioniert.

Wenn ich jedoch dann eine Disconnect durchführen will, bleibt die Anwendung stehn obwohl der Server den Disconnect erhält. Ein anderer Fehler wird nicht produziert, die Anwendung bleibt beim Dispose Befehl stehen.

Folgenden Code zum Connecten und Disconnecten benutze ich:

        public void ZComCon()
        {
            try
            {
                this._Connection = new ZyanConnection(WriteIni.SearchInIni("Net", "NetServer"));
                this.FaceProxy = this._Connection.CreateProxy<IFace>();
            }
            catch (Exception)
            {
                throw new Exception("Die Verbindung zum Z-Server konnte nicht hergestellt werden.");
            }
        }

        public void ZComDis()
        {
            try
            {
                this._Connection.Dispose();
            }
            catch (Exception)
            {
                throw new Exception("Die Verbindung vom Z-Server konnte nicht einwandfrei getrennt werden.");
            }
        }

 

Benutze ich das Dispose irgendwie falsch? Denn Serverseitig funktioniert der Befehl ganz gut.

 

MfG

Tarzipan

Coordinator
Oct 30, 2011 at 5:52 PM

Hallo Tarzipan,

schön dass Dir unser Projekt gefällt.

Zunächst sollten wir Dein Problem näher eingrenzen. Dazu habe ich ein paar Fragen:

  • Mit welcher Zyan-Version arbeitest Du?
  • Bekommst Du irgendeine Exception? (Wenn ja, welche und was steht genau im Stack Trace?)
  • Welches Protocol Setup kommt zum Einsatz? (TcpBinary, TcpCustom, TcpDuplex, HttpCustom, IpcBinary; Wenn Du nichts weiteres angegeben hast, ist es automatisch TcpBinary)
  • Ist Deine Client-Applikation eine WPF oder Windows.Forms-Anwendung?
  • Verwendest Du verteilte Events oder asynchrone verteilte Delegaten (Ähnlich wie im MiniChat-Beispiel)?
  • Arbeitest Du mit mehreren Threads?

Zur Fehlerdiagnose wäre auch noch ein Codeschnipsel interessant, wie Du den ComponentHost erzeugst und konfigurierst.

Viele Grüße

Rainbird

Nov 5, 2011 at 2:45 PM
Hallo,

danke für die Antwort.

-Meine Zyan Version ist 2.1.4216.37977 .

-Leider bekomme ich keine Exception. Fühlt sich so an, als würde man in eine Art Endlosschleife rutschen.

-Das Protocoll ist Default.

-Windows.Forms-Anwendung

-Die Kommunikation ist so wie im MiniChat Bsp.

-Ich arbeite nur mit einem Thread

 

Damit eröffne ich den Server in einer Konsolenanwendung:

private void Run()
        {
            using (host = new ZyanComponentHost("ZServer", Convert.ToInt16(WriteIni.SearchInIni("Net", "NetPort"))))
            {
                host.RegisterComponent<IFace, ZServer>(ActivationType.Singleton);
                Console.WriteLine("Der Server wurde erfolgreich gestartet und wartet auf Verbindungen!");
                host.ClientLoggedOn += new EventHandler<LoginEventArgs>(host_ClientLoggedOn);
                host.ClientLoggedOff += new EventHandler<LoginEventArgs>(host_ClientLoggedOff);
                RunFlag = true;
                System.String command = string.Empty;

                //Dauerschleife um Consolenbefehle abzufragen
                while (RunFlag)
                {
                    command = Console.ReadLine();
                    if (command == "help")
                    {
                        Console.WriteLine("Folgende Commands können benutzt werden:");
                        Console.WriteLine("help - ruft die Hilfe auf");
                        Console.WriteLine("clients - ruft eine Liste der aktuell verbundenen Clients auf");
                        Console.WriteLine("restart - Startet den Server neu und setzt die Clientliste zurück");
                        Console.WriteLine("quit/exit - beendet den Server und das Programm");
                    }
                    else if (command == "clients")
                    {
                        Console.WriteLine("Es sind momentan " + clients + " Clients verbunden.");
                    }
                    else if (command == "restart")
                    {
                        RunFlag = false;
                        host.Dispose();
                        Run();
                    }
                    else if (command == "quit" || command == "exit")
                    {
                        RunFlag = false;
                        Environment.Exit(0);
                    }
                    else
                    {
                        Console.WriteLine(command + " ist kein gültiger Befehl!");
                    }
                }
            }
        }
Coordinator
Nov 10, 2011 at 5:42 AM

Hallo Tarzipan,

bitte schau mal nach bei welcher Zeile Dein Client "hängen" bleibt. Dazu startest Du Deien Client in Visual Studio im Debug-Modus, wartest bis er "hängen" bleibt und wählst dann in Visual Studio im Menü Debugging den Menüpunkt Alle unterbrechen.
Die aktuelle Zeile sollte dann grün hervorgehoben werden.

Das könnte helfen, den Fehler einzugrenzen.

Vermutlich hängt das Problem mit Events/Delegaten zusammen. Bei Windows.Forms darf nur im GUI-Thread direkt auf Formulare und Steuerelemente zugegriffen werden. Methoden die mit entfernten Events/Delegaten verbinden sind, werden aber nicht im GUI-Thread aufgerufen. Deshalb müssen Aufrufe auf Formulare und Steuerelemente mittels Invoke auf den GUI-Thread umgeleitet werden. Das hast Du bestimmt auch das ein oder andere Mal in Deiner Client-Anwendung gemacht.
Wenn der GUI-Thread allerdings gerade beschäftigt ist, wartet der aufrufende Thread, bis der GUI-Thread wieder verfügbar ist. Falls der GUI-Thread aber auf den Thread wartet der auf den GUI-Thread wartet, entsteht eine sogenannte Deadlock-Situation. Beide Threads warten bis in alle Ewigkeit aufeinander.

Ich vermute so ein Deadlock das Problem in Deiner Anwendung ist.

Hast Du in Deiner Client-Anwendung Ereignisprozeduren oder mit Delegaten verbundene Methoden, die direkt oder indirekt ZyanConnection.Dispose() aufrufen?

hast Du in Deiner Client-Anwendung ein Feature integriert, mit dem man dem Server den Befehl zum Beenden geben kann?

Nov 11, 2011 at 9:52 AM
Edited Nov 11, 2011 at 9:57 AM

Hallo Rainbird,

ich habe das Debugging wie beschrieben durchgeführt und es bleibt mit der grünen Zeile hier stehen.

this._Connection.Dispose();

Zu der ersten Frage: Ich erstelle und beende die Verbindung im Moment folgend, Das Main-Forminstaziert die Klasse für Zyan (Code im ersten Post + Beiwerk) mit dem Befehl

this._SingeZCom = ZCom.getInstance();

this._SingeZCom.ZComCon();

Beendet wird die Verbindung mit folgendem Befehl

this._SingeZCom.ZComDis();



Zur zweiten Frage: Nein, ein solches Feature habe ich nicht. Ich wüsste auch nicht wie das geht.

Coordinator
Nov 15, 2011 at 5:57 AM

Hallo Tarzipan,

ich habe in einem kleinen Demoprojekt versucht, Deine Umgebung nachzubauen. Leider konnte ich den Fehler nicht reproduzieren.
Mein Demo-Client macht keine Probleme beim Aufruf von Dispose.

Aber wenn Du möchtest schaue ich mir Deinen Quellcode mal an. Dazu müsstest Du ihn per E-Mail in ein ZIP-Archiv eingepackt an hagen.siegel@the-rainbird.de schicken.

Gruß

Rainbird

Nov 15, 2011 at 11:14 AM

e-Mail ist raus.

Coordinator
Nov 16, 2011 at 7:03 AM
Edited Nov 16, 2011 at 7:06 AM

Ich habe mir Dein Projekt angesehen und den Fehler auch gleich gefunden.
Das Problem wird durch Aufruf von Dispose am Komponentenhost im laufenden Betrieb innerhalb der Funktion host_ClientLoggedOff verursacht.

Du darfst den Host während des ClientLoggedOff-Ereignisses entsorgen. Dieses Ereignis wird innerhalb der Logoff-Methode (serverseitig) gefeuert.
Die Logoff-Methode wird von der clientseitigen ZyanConnection beim abmelden synchron aufgerufen. Wenn während der Ausführung von Logoff der Host entsorgt wird,
kann der Client die Abmeldung nicht abschließen.

 

void host_ClientLoggedOff(object sender, LoginEventArgs e)
{
    Console.WriteLine("Client Verbindung getrennt. " + e.ClientAddress);
    clients--;
    if (clients == 0)
    {
        Console.WriteLine("Keine Verbindungen mehr auf dem Server, starte neu...");
  
        // FOLGENDE DREI ZEILEN AUSKOMMENTIEREN
        host.Dispose();
        RunFlag = false;
        Run();
    }
}

 

Es gibt keinen sinnvollen Grund, warum der Host neu gestartet werden müsste, wenn sich der letzte Client abmeldet. Du kannst den Host einfach laufen lassen und musst ihn nicht zwischendurch neu starten.
Wenn Du die drei Zeilen (wie oben angemerkt) auskommentierst, funktioniert Dein An- und Abmelden einwandfrei.

Von der Zählung der aktiven Clients über erfolgte ClientLoggedOn/ClientLoggedOff-Ereignisse möchte ich Dir auch dringend abraten. Denn Du hast den Fall nicht bedacht, wenn sich ein Client nicht
ordentlich beim Server abmeldet, weil er z.B. einfach ausgeschaltet wird oder das Betriebssystem abstürzt. Dann ist der Client zwar weg, aber es gibt kein ClientLoggedOff-Ereignis, da er sich nie abgemeldet hat.

Wenn die Anzahl der aktiven Clients für Dein Projekt wirklich wichtig ist, solltest Du einen eigenen SessionManager implementieren (einfach von Zyan.Communication.SessionMgmt.SessionManagerBase ableiten).
Deinen selbstgeschriebenen SessionManager musst Du dann beim Erstellen der ZyanComponentHost-Instanz dem Konstruktor übergeben. In einem SessionManager kannst Du die tatsächliche Anzahl der
aktiven Clients (= aktive Sitzungen) genau bestimmen.

Einen Überblick über die Zyan-Sitzungsverwaltung findest Du hier: http://zyan.codeplex.com/wikipage?title=Sitzungsverwaltung&referringTitle=Deutsche%20Dokumentation

Es gibt übrigens schon die Zyan-Version 2.2. Da sind viele Bugs behoben und die Registrierung von Delegaten und Eregnissen ist wesentlich schneller (würde auch Deinem Projekt zugute kommen). Du kannst die neue Version hier runterladen: http://zyan.codeplex.com/releases/view/70590

Nov 17, 2011 at 3:01 PM

Danke Rainbird für die Hilfe!

jetzt funktioniert es.

Ich wollte mir die Infos über die Clients selber verschaffen, da ich mit dem Sessionmanager nicht klar komme. Gibt es Informationen darüber wie man sich z.B. in der Console anzeigen lassen kann welche Clients verbunden sind? Mit z.B. IP-Adresse und ID / Name. Ganz toll wäre es natürlich auch wenn ein Client Infos über andere Clients beantragen kann.

MfG Tarzipan