Laden...

c# .NET Remoting Problem

Erstellt von Lynix vor 20 Jahren Letzter Beitrag vor 19 Jahren 10.192 Views
L
Lynix Themenstarter:in
667 Beiträge seit 2004
vor 20 Jahren
c# .NET Remoting Problem

Hallo zusammen !

Ich habe folgendes Problem mit c# Remoting :

Mein Projekt besteht aus einer Server- (Konsolen-Anwendung) und einer Client-Anwendung (Win32-Anwendung, Windows.Forms).

Desweiteren habe ich eine DLL, die die eigentliche Business-Class meines Servers darstellt, wobei meine Server-Applikation eigentlich nur dazu dient, den Server-Proxy am Leben zu erhalten.

Nun versuche ich über Remote-Events, die durch Aufruf einer entsprechenden Methode im Server ausgelöst werden, Auswirkungen in allen angemeldeten Clients herbeizuführen.

D.h. letztendlich sollen mehrere Instanzen meiner Client-Applikation mit dem Server über ein Firmen-internes Netzwerk verbunden sein.

Meine Applikation liest dabei z.B. per Knopfdruck Einträge aus einer zentralen Datenbank aus, die dann von dem Benutzer der jeweiligen Client-Instanz editiert werden können. Wenn ein Benutzer in seinem Client den Button "Datenbank öffnen" betätigt, wird im Server eine Methode SetDatabaseState aufgerufen.
Diese Methode feuert nun ein Event ab, und dieses Event ist wiederum von allen Clientinstanzen abboniert, so dass die Änderungen (in diesem Fall die Sperrung aller Edit-Boxen) in allen Clients wirsam werden.

So weit, so gut.

Zunächst hatte ich das Problem, dass das Event nur bei dem jeweils zuletzt geöffneten Client angekommen ist, während alle anderen davon nichts mitgekriegt haben.

Ich hab dann komplett auf eine Eventsteuerung verzichtet und stattdessen bei der Registrierung eines Clients, den eigenen Objektpointer (this) mit an den Server übergeben, wodurch ich dann in einer Schleife durch alle angemeldeten Client-Instanzen durchlaufen kann.

Diese Lösung scheint mir aber sehr unschön zu sein. Gibt es nicht stattdessen eine Möglichkeit, mit der wirklich alle Clients von dem Event etwas merken ?

Hier mal die wichtigen Auszüge aus meinem Code, um die Sache etwas zu verdeutlichen:

Klasse ServerApp (in der Main-Funktion meiner Konsolenanwendung) :


TcpChannel myChannel = new TcpChannel(8110);
ChannelServices.RegisterChannel(myChannel);
RemotingConfiguration.RegisterWellKnownServiceType(RemoteObject,"G2FT",WellKnownObjectMode.Singleton);

System.Console.WriteLine("Server running ...");
System.Console.ReadLine();

mein Delegate für das Event sieht so aus :


public delegate void DatabaseStateChanged(int status);

Klasse RemoteObject, in der DLL, die meine Server BC enthält :


public class RemoteObject : MarshalByRefObject
  {
    public void ChangeDatabaseStatus(int status)
    {
       InvokeEvent(status)
    }

    public event DatabaseStateChanged DatabaseStateChangedHandler;
    public event DatabaseStateChanged DatabaseStateChangedEvent
    {
       get
       {
          DatabaseStateChangedHandler = value;
       }

       set
       {
       }
  }

  public void InvokeEvent(int status)
  {
     if(DatabaseStateChangedHandler != null)
       DatabaseStateChangedHandler(status)
  }
}

Im Konstruktor meiner Client-Klasse :


TcpChannel myChannel = new TcpChannel(0);
ChannelServices.RegisterChannel(myChannel);

RemoteObject myServer = (RemoteObject)Activator.GetObject(typeof(RemoteObject), "tcp://localhost:8110/G2FT");

myServer.DatabaseChangedEvent += new DatabaseChangedDelegate(OnDatabaseStatusChanged);


die OnDatabaseStatusChanged - Methode in meines Clients sieht so aus :


public void OnDatabaseChanged(string status)
{
  Button_OpenDatabase.Enabled = false;
  Button_ChangeCurrentEntry.Enabled = false;
  //usw.
}

und letztendlich die Stelle, an der mein Client dem Server mitteilt, dass er das entsprechende Event auslösen soll :


private void On_Button_OpenDatabase_Click(object sender, System.EventArgs e)
{
myServer.ChangeDatabaseStatus(1) // (z.B.)

//ansonsten folgt hier der Code zum Öffnen der Datenbank, die dann in einem TreeView dargestellt wird. 
}

So, also das sind mal die wichtigsten Stellen, also nur die Stellen die zum Rekapitulieren des Problems notwendig sind.
Was nun mit diesem Code passiert ist Folgendes :

Wird in irgendeinem Client der OpenDatabase-Button gedrückt, dann kommt das Event nur in dem zuletzt geöffneten Client an. Alle anderen Clients, auch derjenige in dem der Knopf gedrückt wurde, bleiben unverändert.

Mit meiner neuen Lösung, nämlcih mit der Übergabe der Objektreferenzen der Clients an den Server funktioniert das Ganze dagegen wie es soll. Leider kann ich dann nicht mehr bequem mit Events arbeiten 🙁

Kann mir jemand sagen, was ich hier ändern muss damit das Remote Event überall ankommt ? Bzw. ist das überhaupt möglich ?

Danke schonmal im Voraus für Eure Hilfe !

P.S.:

Es kann sein, dass ein paar Tippfehler drin sind. Ich bin zur Zeit nicht an meinem Arbeitsplatz, und hab den Code grad aus dem Gedächtnis heraus rekonstruiert.

"It is not wise to be wise" - Sun Tzu

502 Beiträge seit 2004
vor 20 Jahren

Ich weiss zwar nicht, ob's was bringt, aber ich spekulier einfach mal wild:

Einerseits könnte es sehr wohl sein, dass dies ein prinzipielles Problem darstellt, weil die Verbindung zwischen Client und Server Applikation über TCP/IP abläuft. TCP/IP ist aber ein "verbindungsloses" Protokoll, was hier soviel bedeutet, dass die Kommunikation immer im Prinzip so abläuft:

  1. Client stellt Anfrage an Server
  2. Server schickt Antwort zurück

Die Folge daraus ist, dass der Client im Prinzip auch ein Server sein muss, wenn er asynchron -sprich: irgendwann- benachrichtigt werden muss, obwohl er keine Anfrage gestellt hat. Folglich ist Deine Lösung im Prinzip die richtige...

Andererseits denke ich aber dass die Lösung, das Ganze über Events zu handlen funktionieren sollte, weil das .net Framework -wenn ich richtig liege- im Prinzip genau das was Du von Hand nachbaust, wenn Du Clientreferenzen speicherst, automatisch macht. Bin mir aber nicht 100%ig sicher, dazu müsstest Du mal in der MSDN wühlen 🙂

Bart Simpson

Praxis ist wenn alles funktioniert und keiner weiss warum.
Theorie ist wenn man alles weiss, aber nichts funktioniert.

Bei uns wird Theorie und Praxis vereint: Nichts funktioniert und keiner weiss warum...

L
Lynix Themenstarter:in
667 Beiträge seit 2004
vor 20 Jahren

Danke für die Antwort !

Ich denke trotzdem, dass es irgendwie funktionieren muss, denn es kann ja nicht Sinn und Zweck der Sache sein, dass der Client selbst die Tätigkeit eines Servers ausführt. Wenn z.B. auf einem Client-Rechner eine Firewall aktiv ist, die spezielle Ports blockiert, würde das Ganze dann auch z.B. nicht mehr funktionieren.

In der MSDN hab ich zu dem Thema nicht viel gefunden und im Netz sind auch nur Beispiele zu finden, die das Ganze mit einem Server und EINEM Client beschreiben.

Hm, ich bin momentan etwas ratlos...

Mal eine andere Frage : Was ist denn genau der Unterschied zwischen TcpChannel() und TcpClientChannel() / TcpServerChannel() ? Kann es vielleicht damit etwas zu tun haben ?

"It is not wise to be wise" - Sun Tzu

L
Lynix Themenstarter:in
667 Beiträge seit 2004
vor 20 Jahren

Hat sich erledigt. Ich hab das Problem gelöst. Man benötigt zusätzlich eine Callback-Klasse, die ebenfalls von MarshalByRefObject abgeleitet wird, auf die alle Delegaten verweisen können. Sozusagen als Zwischenstück zwischen den einzelnen Clients.

"It is not wise to be wise" - Sun Tzu

N
25 Beiträge seit 2004
vor 19 Jahren

Original von Mr. Bart Simpson
... TCP/IP ist aber ein "verbindungsloses" Protokoll...

Naja, ich weiß ja nicht ob du hier vielleicht was anderes meinst, aber TCP/IP ist ein verbindungsorientiertes Protokoll.Was bedeutet: Es wird eine feste Verbindung zwischen den Rechnern hergestellt; die Rechner "reden" miteinander und anschließend wird die Verbindung wieder abgebaut.

Die Folge daraus ist, dass der Client im Prinzip auch ein Server sein muss...

Ich würde mal sagen, dass Clients Clients sind und Server Server. Du könntest dir vielleicht mal das Observer Pattern anschauen. Ich denke mal, dass du so was benötigst.

Gruß
Frank