Laden...

Remoting: Exception, bei Versenden von Objekten :(

Erstellt von Sieben vor 17 Jahren Letzter Beitrag vor 17 Jahren 2.699 Views
Sieben Themenstarter:in
140 Beiträge seit 2006
vor 17 Jahren
Remoting: Exception, bei Versenden von Objekten :(

Huhu,

ich check des ne ganz... also ich hab nen Server und ein Client. Dazu ein Objekt was ich zwischen Server und Client verschicken will...

Die Verbindung klappt soweit so gut. Ich kann auch die Funktionen soweit aufrufen und Parameter mit geben. Das Problem ist, das wenn ich einer Funktion ein Objekt mitgeben will, bringt er mir ne Exception "Auf den Typ System.Runtime.Remoting.ObjRef kann aufgrund von Sicherheitseinschränkungen nicht zugegriffen werden."...

Woran liegt?? Mach ich was falsch??

Hier mald er Code... ist von nem Tutorial ausm Internet:

Das Client Projekt:
Client
hat eine Referenz auf das Server Projekt


class Program
  {
    static void Main(string[] args)
    {
      TcpClientChannel tcpChnl = new TcpClientChannel();
      ChannelServices.RegisterChannel(tcpChnl);
      Account acc = (Account)Activator.GetObject(
      typeof(Account), "tcp://localhost:45100/Account.rem");
      Account acc2 = new Account();
      acc2.Initialize("It'sme", 10000.00);
      
      acc.Initialize("It'sme", 10000.00);
      acc.Transfer(acc2, 12); // <- Hier tritt die Exception auf
      Console.WriteLine("AccountNo.:\t" + acc.Id_ +
      "\nOwner:\t\t" + acc.Owner_ +
      "\nBalance:\t" + acc.Balance_);
      Console.ReadLine();
    }

Das Server Projekt
Server


    static void Main(string[] args)
    {
      TcpServerChannel tcpChnl= new TcpServerChannel(45100);
      ChannelServices.RegisterChannel(tcpChnl);
      RemotingConfiguration.RegisterWellKnownServiceType(
      typeof(Account),
      "Account.rem",
      WellKnownObjectMode.Singleton);
      Console.ReadLine();
    }

Account Interface


  public class Account : MarshalByRefObject
  {
    public int Id_;
    public string Owner_;
    public double Balance_;
    public bool Locked_;

    public void Initialize(string own, double bal) 
    {
      this.Owner_ = own;
      this.Balance_ = bal;
    }
    public void Deposit(double amo) { }
    public void Withdraw(double amo) { }
    public void Transfer(Account acc, double amo) {}
  }

Kann mir jemand sagen wo der Fehler liegt, oder was ich prinzipiell anders machen muss??

gruß Sieben

Nur die Kogge schwimmt! 😁

3.728 Beiträge seit 2005
vor 17 Jahren
Interface

Du hast den Unterschied zwischen einem Objekt, welches per RPC angesprochen werden kann, und einem Objekt welches die zu übertragenden Daten enthält nicht verstanden. Außerdem ist Dein Interface kein Interface sondern eine Klasse. Ein Interface würde so aussehen:

public interface IAccount
{
    // ...
}

Du musst zwischen folgenden Arten von Klassen bei Remoting unterscheiden:*Von MarshalByref abgeleitete Klassen können entfernt angesprochen werden, leben aber nur im Prozeß des Hosts (also nur auf dem Server) *Klassen, die serialisierbar sind und in seruialisierter Form Daten zwischen Client und Host übertragen (Diese Klassen müssen entweder ISerializable implementieren oder mit dem [Serializable] Attribut versehen werden!)

Du musst Daten und Funktionalität also voneinander trennen. Das würde bedeuten, dass Du aus der Klasse Account folgendes machst:*Eine Klasse AccountManager, die von MarshalByRefObject abgeleitet ist *Eine Klasse Account, die nur die Properties enthält und mit [Serializable] versehen ist *Eine Schnittstelle IAccountManager in einer separaten DLL, die auf den Client kopiert wird (Der Client soll die Implementierung nicht haben!)

Hier nochmal die Grundlagen von .NET Remoting in Bild und Ton:
http://www.microsoft.com/germany/msdn/nettv/folge1.mspx

Sieben Themenstarter:in
140 Beiträge seit 2006
vor 17 Jahren

Huhu,

jup des klappt so Einwandfrei 🙂

Was mir noch aufgefallen ist, das ich die implementation der Account Klasse beiden Projekten mitteilen muss. Wenn ich dem Client die Account Klasse nur als Interface hinstelle schmeißt er ne Exception das irgendein Assenbly fehlt, ist das korrekt so??

Wenn ja könnt ich die Impl ja mit in die Interface DLL packen?!

Noch ne frage, wie erreiche ich es, das mein Server mehrere Clients verwalten kann und einzelne Clients über Aktionen anderer Clients informiere?

Gibt es da eine Möglichkeit?

gruß Sieben!

Nur die Kogge schwimmt! 😁

S
8.746 Beiträge seit 2005
vor 17 Jahren

Du musst das Interface in eine eigene DLL packen.

Sieben Themenstarter:in
140 Beiträge seit 2006
vor 17 Jahren

Huhu,

hm... ich glaub da ging was durcheinander... also momentan hab ich 3 Projekte:

  1. ServerApp
  2. ClientApp
  3. AppDLL mit 2 Interfaces ( IServerRemObj, IMessageObj )

Die ClientApp hat eine Referenz auf die AppDLL mit den 2 Interfaces.

Die ServerApp hat eine Referenz auf die AppDLL und hat 2 Klassen ( "ServerRemObj : MarshalByRefObject, ServerManager" und "Message : IMyMessage" ) die jeweils das Interface implentieren.
Das "ServerRemObj" soll dazu dienen vom Client aus, funktionen des Server aufzurufen. Das "MessageObj" soll dazu dienen Daten zwischen Client und Server auszutauschen.

Das Problem ist jetzt, das ich eine Exception beim Client bekomme, das er mein Objekt was ich schicken will nicht kennt:

"{System.Runtime.Serialization.SerializationException: Die Assembly TestApp, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null kann nicht gefunden werden."

Wenn ich nun in der AppDLL Klasse statt das Interface "IMessageObj" die Klasse "MessageObj" implementiere funktioniert das ganze. Was aber ziemlich blöd ist, denn wenn sich die Implementation des Message Objekts ändern, dann brauch der Client ne neue DLL...

  1. Kann man das ganze noch etwas anders lösen??
  2. Wie kann man Serverseitig die Clients speichern und verschiedene Nachrichten an die jeweiligen Clienten schicken...

gruß Sieben

Nur die Kogge schwimmt! 😁

3.728 Beiträge seit 2005
vor 17 Jahren
Packaging

Dein Packaging stimmt noch nicht. Du solltest alles so einteilen:

Interface Assembly:

Enthält IServerRemObj-Schnittstelle

Assembly für gemeinsam genutzte Datentypen:

Enthält MyMessage-Klasse (Eine Schnittstelle ist dafür nicht erforderlich)

Geschäftslogik-Assembly:

Enthält Verweis auf die Interface Assembly
Enthält ServerRemObj-Klasse

Client Assembly:

Enthält Verweis auf die Interface-Assembly
Enthält Verweis auf die Assembly für gemeinsam genutzte Datentypen

Host-Assembly

Enthält Verweis auf die Interface-Assembly
Enthält Verweis auf die Geschäftslogik Assembly

Du hast folgenden Fehler gemacht: Die Assembly, die Deine serialisierbare Message enthält, muss natürlich extra sein. Außerdem muss auch die Implementierung von MyMessaeg auf dem Client verfügbar sein (Wie sollte sonst das deserialisieren klappen?).

Sieben Themenstarter:in
140 Beiträge seit 2006
vor 17 Jahren

Huhu,

jup genauso dacht ich mir des 🙂

Jetzt mal ne weitere Frage... wie mache ich des, das der Server sich mehrere Clients speichern kann und diese von sich aus benachrichtigen kann?

Der Client braucht wieder ein Interface, des is klar... aber wie speichere mehrere Clients im Server??

Der Client müsste dann ja von MarshalByRefObject und mit Serialisable versehen werden...

gruß Sieben

Nur die Kogge schwimmt! 😁

3.728 Beiträge seit 2005
vor 17 Jahren
Clientaktivierte Objekte

Du schickst keine Clients zum Server. Und der Server "speichert" auch keine Clients. Eine Klasse ist entweder Serialisierbar (dann, wenn es eine Klasse ist, die zu transportierende Daten enthält) oder sie erbt von MarshalByRefObject (dann, wenn sie entfernte Funktionsaufrufe unterstützen soll). Wenn ich Dich richtig verstanden habe, möchtest Du, dass sich Clients beim Server registrieren (man könnte auch Sitzungsverwaltung dazu sagen) und dann von diesem mit Informationen versorgt werden. Dazu ist folgendes zu sagen:

Zunächst können Objekte mit Remoting auch clientaktiviert vom Host angeboten werden. Jeder Client bekommt dann seine eigene Instanz auf dem Host. Das gefährliche daran ist, dass Erstellung und Zerstörung (also die Objektlebensdauer) vom Client gesteuert werden. Der Client bindet damit Ressourcen auf dem Server. Je mehr Clients, desto mehr Serverlast. Das kann sich sehr negativ auf die Leistung auswirken, wenn viele Clients beteiligt sind. Bei serveraktivierten Objekten (SingleCall/Singelton) ist die Anzahl der Clients theoretisch Wurst (Ob 1 Client 100 Anfragen stellt oder ob 100 Clients 1 Anfrage stellen ist das selbe).

Das mit dem Benachrichtigen ist ebenfalls mit großer Vorsicht zu genießen, weil die Clients auch als Server auftreten müssen. Das kann kompliziert werden. Folgende Artikel beschäftigen sich mit Events, Callbacks etc. per Remoting:
http://www.microsoft.com/germany/msdn/library/net/VerwendenVonAsynchronenRemoteprozeduraufrufenMitClientServerAnwendungen.mspx
http://www.codeproject.com/csharp/RemotingAndEvents.asp