Laden...

Design einer Remoting-Anwendung

Erstellt von Daedalus vor 17 Jahren Letzter Beitrag vor 17 Jahren 5.097 Views
D
Daedalus Themenstarter:in
8 Beiträge seit 2006
vor 17 Jahren
Design einer Remoting-Anwendung

Moin zusammen!

Ich habe da mal eine Frage zum grundlegenden Design einer Remoting-Anwendung.

Ich versuche mich an einer Client-/Server-Anwendung, die bislang grob den folgenden Aufbau hat:*Klasse formClient: GUI des Clients (behandelt z.B. die Eingaben des Benutzers und übergibt diese der Klasse Client) *Klasse formServer: GUI des Servers (verschiedene Server-Einstellungen wie z.B. Portnr. etc.) *Klasse Client: Logik des Clients ("schickt" u.a. die Eingaben an den Server) *Klasse Server: Logik des Servers (Erstellen des TCPChannels etc.) *Klassen mit Geschäftslogik (verschiedene Berechnungen) *Interface IServer

Der Server soll dem Client nun die Funktionen aus den Geschäftslogik-Klassen zur Verfügung stellen. Dazu habe ich nun einige Tutorials, Hilfen etc. gelesen (z.B. auch in diesem Forum) in denen empfohlen wird, auf Client-Seite nur mit Interfaces zu arbeiten, damit bei internen Änderungen auf der Server-Seite der Client nicht upgedatet werden muss.

Meine Frage ist nun, wie ich das bei mehreren Geschäftslogik-Klassen realisieren sollte. Wenn ich nun z.B. die Klasse Berechnung habe, deren Methode BerechneEtwas() ich dem Client zur Verfügung stellen will, wie würde man das am "saubersten" implementieren? *Nur den Server marshallen und hier eine Methode BerechneEtwas() anlegen, die einfach nur die Methode in Klasse Berechnung aufruft und deren Ergebnis zurückgibt. Diese müsste dann auch im Interface IServer angelegt werden. Das hätte zur Folge, dass in der Klasse Server hauptsächlich Methoden enthalten wären, die semantisch hier gar nicht hingehören. *Nur den Server marshallen und für jede gwünschte Klasse eine Methode anlegen, die ein Objekt dieser Klasse zurückgibt, mit dem der Client weiterarbeiten kann. Dazu muss jede Klasse serialisierbar und dem Client über ein Interface bekannt sein. *Jede Geschäftslogik-Klasse einzeln marshallen und entsprechend mehrere Interfaces bereitstellen. Hierzu bräuchte man (meines Wissens nach) mehrere Channel, was ich eigentlich nicht will.

Des Weiteren habe ich noch eine Frage zum Aufruf der Methoden in den Formularen. Wenn ich nun bspw. nach dem Klicken auf einen Button im Client-Formular dessen Daten an den Server zum Verarbeiten senden will, habe ich in Klasse formClient folgende zwei Möglichkeiten:*Im Formular formClient referenziere ich ein Client-Objekt, das wiederum ein Server-Objekt referenziert, und rufe dessen Methode mit den Parametern auf: this.client.server.TuWas(Parameter). Dabei greife ich auf das Attribut server zu, was nicht so sauberer Stil ist. *Im Formular formClient rufe ich die Methode TuWas(Parameter) des Cliens auf. Diese Methode ruft die Methode TuWas(Parameter) des Servers auf. Hierbei entsteht eine Verkettung von Methodenaufrufen, was ich auch irgendwie sehr komisch finde.

Bei beiden Fällen wäre dann noch zu klären, ob letztlich überhaupt der Server die Methode TuWas() anbietet oder diese an die Geschäftslogik weiterreicht (siehe erste Frage).

Ich hoffe, ich konnte mein Problem einigermaßen verständlich machen. Das Remoting an sich (per TCPChannel etc.) klappt einwandfrei, aber die logische und semantisch korrekte Abbildung der Methoden in den entsprechenden Klassen ist im Moment mein Hauptproblem. Ich hätte gerne einen schönen, sauberen Code 🙂

Oh, und wenn noch Zeit ist: Ist es möglich, Formulare auf dem Server zu implementieren, die dann auf dem Client angezeigt werden? Also quasi ein Marshaling von Formularen, die auf dem Server geändert werden können, aber auf dem Client (z.B. mit Hilfe von Interfaces) nur angezeigt werden, ohne dass dieser den Code kennen muss. Ich denke da an einen Client, der nur einige Menüeinträge umfasst, beim Klick auf diese die entsprechenden Formulare jedoch vom Server lädt und anzeigt.

Danke schonmal im Voraus an alle, die sich hierfür Zeit nehmen! 🙂

Gruß
Daedalus

3.728 Beiträge seit 2005
vor 17 Jahren
Aufbau

Ich verstehe nicht was Du mit "den Server marshallen" meinst. Eine Remoting-Anwendung bestht aus folgenden Teilen:*Host (das meinst Du warscheinlich mit Server) *Vom Host gehostete Geschäftslogik, die Clients angeboten wird *Schnittstellen zu den angebotenen Geschäftsklassen in separaten Assemblies *Client (Irgendein Stück Software, welches die Geschäftslogik auf dem Remoting Host nutzt)

Der Host tut außer dem veröffentlichen der Klassen eigentlich nix. Man braucht ihn aber als Lebensraum-Prozess für die Objekte, die per RPC erzeugt werden.

In den meisten Fällen werden die Geschäftslogik-Klassen (also die Typen, keine Instanzen) serveraktiviert als SingleCall oder Singleton (Ja nach Einsatz) veröffentlicht. Jede Klasse hat ihre eigene Schnittstelle. Das geht alles mit einem Channel. Über den Channel gehen Clientanfragen beim Host ein. Für jede Anfrage wird ein neuer Thread erzeugt, der die remote aufgerufene Funktion abarbeitet. Bei SingleCall Aktivierung wird eine Instanz der angefragten Klasse erzeugt, die Funktion ausgeführt und die Instanz anschließend sofort wieder entsorgt. Bei Singeton verwenden alle Clientanfragen eine einzige Instanz. Diese wird bei der ersten Clientanfrage erzeugt. Bei verwendung der Singleton Aktivierung sollte man unbedingt threadsicher programmieren.

Alternativ gibt es auch die Möglichkeit, dass der Host Instanzen von Geschäftslogik-Klassen erzeugt und diese direkt veröffentlicht. Dazu ist allerdings etwas Handarbeit nötig. Wird aber meistens nicht benötigt (Meinst Du vielleicht das, mit "den Server marshallen"?).

Einige Sachen sind in Deinem Beispiel nicht schlüssig, weil Du alles Client oder Server nennst. Da gibts das frmClient und die Client-Klasse, usw.. In einem realen Projekt würden die Klassen niemals so heißen. Wenn die Client-Klasse nur Funktionsaufrufe 1:1 an den Remoting-Host weiterleitet, ist sie unnötig. Dann kannst Du das Zeug auch direkt aufrufen. Mehr Sinn macht eine Strukturierung des Client-Code im Sinne des MVC Pattern (Dazu findest Du hier im Forum einiges). Sinnvoll wäre die Client-Klasse auch wieder im Sinne einer Factory, die sich zentral um das erzeugen der Proxies und das casten in die benötigten Interfaces kümmern. Außerdem ist so eine Factory auch gut geeigent, um die Verarbeitung von Konfigurationseinstellungen aus dem Formular-Code heraus zu halten. Bei Remoting fällt mindestens eine Konfig-Einstellung an: Der URL zum Remoting-Host.

Formulare auf dem Server ergeben keinen Sinn, da man sie auf dem Client nicht sehen würde. Die Formulare würden letztendlich im Grafikspeicher des Servers landen. Versteuerungen von Formularen auf einem Server ist auch keine gute Architektur für robuste verteilte Anwendungen. Du kannst natürlich eine Komponente auf dem Remoting Host veröffentlichen, die dem Client konfigurierbare Menüeinträge (z.B. aus einer Datenbank) oder ähnliches anbietet. Das wird gerne gemacht, wenn Anwendungen zentral für mehrere Benutzer angepast oder erweitert werden sollen. Der Client könnte sogar automatisch PlugIns nachlagen etc.

N
61 Beiträge seit 2005
vor 17 Jahren

Hast du dir schonmal das Lernvideo von MSDN angesehen?
Da wird alles erklärt, wenn man noch nichts von .Net Remoting weiß.

Hier isses nochmal.

D
Daedalus Themenstarter:in
8 Beiträge seit 2006
vor 17 Jahren
Diagramm

Moin!

Erstmal danke für die umfangreiche Antwort.

Zu meiner zweiten Frage: Das habe ich mir schon fast gedacht. Bei meinen Versuchen war es nämlich genau so: Die Formulare wurden auf dem Host angezeigt... naja, egal 🙂

Zur ersten Frage: Ich habe mich wohl ein wenig umständlich ausgedrückt. Im Prinzip kann man in den Ausführungen "Server" durch "Host" ersetzen 🙂

Ich habe mal zur besseren Anschaulichkeit ein kleines Diagramm meiner Anwendung angefügt:

Ablauf der Kommunikation:*Anwender klickt Button "Verbinden" in formClient *formClient.btnVerbindeClick wird ausgeführt *formClient.client wird als Client instantiiert und verbindeMitServer aufgerufen. Hier wird nun der Channel aufgebaut und das Host-Objekt instantiiert, das über formClient.client.host angesprochen werden kann. *Das erfolgreiche Aufbauen der Verbindung wird über TesteVerbindung
geprüft, und das funktioniert auch.

Ausschnitt aus "Client.cs": VerbindeMitServer um die Verbindung aufzubauen:


this.chan = new TcpChannel(0);
ChannelServices.RegisterChannel(this.chan);
object obj = RemotingServices.Connect(typeof(IHost), "tcp://adresse");
this.host = obj as IHost;
this.IstVerbunden = this.host.TesteVerbindung();

Wie stelle ich dem Client aber nun die Methoden BerechneWas1 zur Verfügung, wenn der Anwender auf den Button "BerechneWas1" klickt? Also wie "verknüpfe" ich die Interfaces auf dem Client mit den tatsächlichen Objekten auf dem Host?

Ich habe doch auch dem Client nur Zugriff auf das Host-Objekt!? Muss ich also...*... das Host-Interface und auch die entsprechende Klasse mit Methoden versehen wie BerechneWas1, die dann die entsprechenden Methoden in den anderen Klassen aufrufen und deren Ergebnisse zurückgeben? *... komplette Objekte übergeben, z.B. mit einer Methode GetBerechnung1 auf dem Host?

Im ersten Fall sähe meine Methode btnBerechneWas1Click in formClient vereinfacht wie folgt aus:


double ergebnis = this.client.host.BerechneWas1();

Und das finde ich seltsam wegen dem Zugriff auf client.host und auch, weil die Interfaces für die Berechnungsklassen eigentlich überflüssig wären.

Beim zweiten Ansatz bekomme ich Probleme beim "Übergeben" der Objekte, wie du auch schon gesagt hast.

Also irgendwie habe ich das Ganze wohl noch nicht so richtig verstanden... 🙂

Das mit dem Lernvideo habe ich gerade erst gesehen. Schaue ich mir mal eben an...

Grüße
Deadalus

D
Daedalus Themenstarter:in
8 Beiträge seit 2006
vor 17 Jahren
Idee

Mhhh... die Seite mit dem Video kannte ich schon, aber beim zweiten Lesen ist mir dann doch vielleicht die Lösung aufgefallen 😉

Muss ich die Sache vielleicht wie folgt lösen?

Ausschnitt aus "Client.cs":


this.chan = new TcpChannel(0);
ChannelServices.RegisterChannel(this.chan);

object host = RemotingServices.Connect(typeof(IHost), "tcp://adresse/Host");
this.host = obj as IHost;
this.IstVerbunden = this.host.TesteVerbindung();

object berechnung1 = RemotingServices.Connect(typeof(IBerechnung1), "tcp://adresse/Berechnung1");
this.berechnung1 = obj as IBerechnung1;

Dann hätte ich einfach mehrere Remote-Objekte lokal verfügbar und könnte sie über die Interfaces ansprechen.

Hatte ich die ganze Zeit über ein (nicht gerade kleines) Brett vor'm Kopf und es ist tatsächlich so einfach? 🙂

Grüße
Daedalus

3.728 Beiträge seit 2005
vor 17 Jahren
Ich kenn das anders

Um ein vom Host angebotenes Objekt als Proxy auf dem Client zu bekommen, gehe ich normal so vor:

IDemo obj=(IDemo)Activator.GetObject(typeof(IDemo),"tcp://host:12345/AppName/ClassName");
D
Daedalus Themenstarter:in
8 Beiträge seit 2006
vor 17 Jahren
Andere Möglichkeit

Ja, deine Methode habe ich auch in den meisten Beispielen gesehen und verwende sie jetzt auch selbst. "Meine" Methode habe ich aus dem Buch von O'Reilly: "Programmieren mit C#".

Gruß
Daedalus